Merge branch 'feature/v4' of https://github.com/tidusjar/ombi into feature/v4

pull/3703/head
Jamie Rees 4 years ago
commit e36b1dba3d

@ -102,12 +102,12 @@ We are planning to bring back these features in V3 but for now you can find a li
| Lidarr | Yes | No |
# Feature Requests
Feature requests are handled on FeatHub.
Feature requests are handled on Feature Upvote.
Search the existing requests to see if your suggestion has already been submitted.
(If a similar request exists, give it a thumbs up (+1), or add additional comments to the request)
(If a similar request exists, please vote, or add additional comments to the request)
#### [![Feature Requests](https://cloud.githubusercontent.com/assets/390379/10127973/045b3a96-6560-11e5-9b20-31a2032956b2.png)](http://feathub.com/tidusjar/Ombi)
#### [![Feature Requests](https://cloud.githubusercontent.com/assets/390379/10127973/045b3a96-6560-11e5-9b20-31a2032956b2.png)](https://features.ombi.io)
# Preview

@ -230,6 +230,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<IIssuesPurge, IssuesPurge>();
services.AddTransient<IResendFailedRequests, ResendFailedRequests>();
services.AddTransient<IMediaDatabaseRefresh, MediaDatabaseRefresh>();
services.AddTransient<IArrAvailabilityChecker, ArrAvailabilityChecker>();
}
}
}

@ -111,7 +111,7 @@ namespace Ombi.Notifications.Agents
{
if (requestedUser.HasValue())
{
fields.Add(new DiscordField { name = "Requsted By", value = requestedUser, inline = true });
fields.Add(new DiscordField { name = "Requested By", value = requestedUser, inline = true });
}
}
if (model.Data.TryGetValue("DenyReason", out var denyReason))

@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Ombi.Core;
using Ombi.Helpers;
using Ombi.Hubs;
using Ombi.Notifications.Models;
using Ombi.Schedule.Jobs.Plex.Models;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests;
using Quartz;
namespace Ombi.Schedule.Jobs.Radarr
{
public class ArrAvailabilityChecker : IArrAvailabilityChecker
{
public ArrAvailabilityChecker(
IExternalRepository<RadarrCache> radarrRepo,
IExternalRepository<SonarrCache> sonarrRepo,
IExternalRepository<SonarrEpisodeCache> sonarrEpisodeRepo,
INotificationHelper notification, IHubContext<NotificationHub> hub,
ITvRequestRepository tvRequest, IMovieRequestRepository movies,
ILogger<ArrAvailabilityChecker> log)
{
_radarrRepo = radarrRepo;
_sonarrRepo = sonarrRepo;
_sonarrEpisodeRepo = sonarrEpisodeRepo;
_notification = notification;
_hub = hub;
_tvRequest = tvRequest;
_movies = movies;
_logger = log;
}
private readonly IExternalRepository<RadarrCache> _radarrRepo;
private readonly IExternalRepository<SonarrCache> _sonarrRepo;
private readonly ILogger<ArrAvailabilityChecker> _logger;
private readonly IExternalRepository<SonarrEpisodeCache> _sonarrEpisodeRepo;
private readonly INotificationHelper _notification;
private readonly IHubContext<NotificationHub> _hub;
private readonly ITvRequestRepository _tvRequest;
private readonly IMovieRequestRepository _movies;
public async Task Execute(IJobExecutionContext job)
{
await ProcessMovies();
await ProcessTvShows();
}
private async Task ProcessMovies()
{
var availableRadarrMovies = _radarrRepo.GetAll().Where(x => x.HasFile).ToImmutableHashSet();
var unavailableMovieRequests = _movies.GetAll().Where(x => !x.Available).ToImmutableHashSet();
var itemsForAvailability = new List<AvailabilityModel>();
foreach (var movieRequest in unavailableMovieRequests)
{
// Do we have an item in the radarr list
var available = availableRadarrMovies.Any(x => x.TheMovieDbId == movieRequest.TheMovieDbId);
if (available)
{
_logger.LogInformation($"Found move '{movieRequest.Title}' available in Radarr");
movieRequest.Available = true;
movieRequest.MarkedAsAvailable = DateTime.UtcNow;
itemsForAvailability.Add(new AvailabilityModel
{
Id = movieRequest.Id,
RequestedUser = movieRequest.RequestedUser != null ? movieRequest.RequestedUser.Email : string.Empty
});
}
}
if (itemsForAvailability.Any())
{
await _hub.Clients.Clients(NotificationHub.AdminConnectionIds)
.SendAsync(NotificationHub.NotificationEvent, "Radarr Availability Checker found some new available movies!");
await _movies.SaveChangesAsync();
}
foreach (var item in itemsForAvailability)
{
await _notification.Notify(new NotificationOptions
{
DateTime = DateTime.Now,
NotificationType = NotificationType.RequestAvailable,
RequestId = item.Id,
RequestType = RequestType.Movie,
Recipient = item.RequestedUser
});
}
}
public async Task ProcessTvShows()
{
var tv = await _tvRequest.GetChild().Where(x => !x.Available).ToListAsync();
var sonarrEpisodes = _sonarrEpisodeRepo.GetAll().Where(x => x.HasFile);
foreach (var child in tv)
{
var tvDbId = child.ParentRequest.TvDbId;
IQueryable<SonarrEpisodeCache> seriesEpisodes = sonarrEpisodes.Where(x => x.TvDbId == tvDbId);
if (seriesEpisodes == null || !seriesEpisodes.Any())
{
continue;
}
//if (!seriesEpisodes.Any())
//{
// // Let's try and match the series by name
// seriesEpisodes = sonarrEpisodes.Where(x =>
// x.EpisodeNumber == child.Title &&
// x.Series.ReleaseYear == child.ParentRequest.ReleaseDate.Year.ToString());
//}
var availableEpisode = new List<AvailabilityModel>();
foreach (var season in child.SeasonRequests)
{
foreach (var episode in season.Episodes)
{
if (episode.Available)
{
continue;
}
var foundEp = await seriesEpisodes.AnyAsync(
x => x.EpisodeNumber == episode.EpisodeNumber &&
x.SeasonNumber == episode.Season.SeasonNumber);
if (foundEp)
{
availableEpisode.Add(new AvailabilityModel
{
Id = episode.Id
});
episode.Available = true;
}
}
}
//TODO Partial avilability notifications here
if (availableEpisode.Any())
{
//await _hub.Clients.Clients(NotificationHub.AdminConnectionIds)
// .SendAsync(NotificationHub.NotificationEvent, "Sonarr Availability Checker found some new available episodes!");
await _tvRequest.Save();
}
//foreach(var c in availableEpisode)
//{
// await _tvRepo.MarkEpisodeAsAvailable(c.Id);
//}
// Check to see if all of the episodes in all seasons are available for this request
var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available));
if (allAvailable)
{
await _hub.Clients.Clients(NotificationHub.AdminConnectionIds)
.SendAsync(NotificationHub.NotificationEvent, "Sonarr Availability Checker found some new available Shows!");
child.Available = true;
child.MarkedAsAvailable = DateTime.UtcNow;
_logger.LogInformation("[ARR_AC] - Child request {0} is now available, sending notification", $"{child.Title} - {child.Id}");
// We have ful-fulled this request!
await _tvRequest.Save();
await _notification.Notify(new NotificationOptions
{
DateTime = DateTime.Now,
NotificationType = NotificationType.RequestAvailable,
RequestId = child.Id,
RequestType = RequestType.TvShow,
Recipient = child.RequestedUser.Email
});
}
}
await _tvRequest.Save();
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

@ -0,0 +1,6 @@
namespace Ombi.Schedule.Jobs.Radarr
{
public interface IArrAvailabilityChecker : IBaseJob
{
}
}

@ -49,29 +49,29 @@ namespace Ombi.Schedule.Jobs.Radarr
// Let's remove the old cached data
using (var tran = await _ctx.Database.BeginTransactionAsync())
{
await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM RadarrCache");
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM RadarrCache");
tran.Commit();
}
var movieIds = new List<RadarrCache>();
foreach (var m in movies)
{
if(m.monitored)
{
if (m.tmdbId > 0)
if (m.monitored || m.hasFile)
{
movieIds.Add(new RadarrCache
if (m.tmdbId > 0)
{
TheMovieDbId = m.tmdbId,
HasFile = m.hasFile
});
}
else
{
Logger.LogError("TMDBId is not > 0 for movie {0}", m.title);
movieIds.Add(new RadarrCache
{
TheMovieDbId = m.tmdbId,
HasFile = m.hasFile
});
}
else
{
Logger.LogError("TMDBId is not > 0 for movie {0}", m.title);
}
}
}
}
using (var tran = await _ctx.Database.BeginTransactionAsync())
{
@ -81,6 +81,8 @@ namespace Ombi.Schedule.Jobs.Radarr
tran.Commit();
}
}
await OmbiQuartz.TriggerJob(nameof(IArrAvailabilityChecker), "DVR");
}
catch (System.Exception ex)
{

@ -11,6 +11,7 @@ using Ombi.Api.Sonarr;
using Ombi.Api.Sonarr.Models;
using Ombi.Core.Settings;
using Ombi.Helpers;
using Ombi.Schedule.Jobs.Radarr;
using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Context;
using Ombi.Store.Entities;
@ -33,7 +34,7 @@ namespace Ombi.Schedule.Jobs.Sonarr
private readonly ISonarrApi _api;
private readonly ILogger<SonarrSync> _log;
private readonly ExternalContext _ctx;
public async Task Execute(IJobExecutionContext job)
{
try
@ -50,49 +51,84 @@ namespace Ombi.Schedule.Jobs.Sonarr
var ids = sonarrSeries.Select(x => x.tvdbId);
using (var tran = await _ctx.Database.BeginTransactionAsync())
{
await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SonarrCache");
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM SonarrCache");
tran.Commit();
}
var existingSeries = await _ctx.SonarrCache.Select(x => x.TvDbId).ToListAsync();
//var entites = ids.Except(existingSeries).Select(id => new SonarrCache { TvDbId = id }).ToImmutableHashSet();
var entites = ids.Select(id => new SonarrCache { TvDbId = id }).ToImmutableHashSet();
await _ctx.SonarrCache.AddRangeAsync(entites);
entites.Clear();
using (var tran = await _ctx.Database.BeginTransactionAsync())
{
await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SonarrEpisodeCache");
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM SonarrEpisodeCache");
tran.Commit();
}
foreach (var s in sonarrSeries)
{
if (!s.monitored)
if (!s.monitored || s.episodeFileCount == 0) // We have files
{
continue;
}
_log.LogDebug("Syncing series: {0}", s.title);
var episodes = await _api.GetEpisodes(s.id, settings.ApiKey, settings.FullUri);
var monitoredEpisodes = episodes.Where(x => x.monitored || x.hasFile);
//var allExistingEpisodes = await _ctx.SonarrEpisodeCache.Where(x => x.TvDbId == s.tvdbId).ToListAsync();
// Add to DB
_log.LogDebug("We have the episodes, adding to db transaction");
using (var tran = await _ctx.Database.BeginTransactionAsync())
{
await _ctx.SonarrEpisodeCache.AddRangeAsync(monitoredEpisodes.Select(episode =>
var episodesToAdd = monitoredEpisodes.Select(episode =>
new SonarrEpisodeCache
{
EpisodeNumber = episode.episodeNumber,
SeasonNumber = episode.seasonNumber,
TvDbId = s.tvdbId,
HasFile = episode.hasFile
}));
});
//var episodesToAdd = new List<SonarrEpisodeCache>();
//foreach (var monitored in monitoredEpisodes)
//{
// var existing = allExistingEpisodes.FirstOrDefault(x => x.SeasonNumber == monitored.seasonNumber && x.EpisodeNumber == monitored.episodeNumber);
// if (existing == null)
// {
// // Just add a new one
// episodesToAdd.Add(new SonarrEpisodeCache
// {
// EpisodeNumber = monitored.episodeNumber,
// SeasonNumber = monitored.seasonNumber,
// TvDbId = s.tvdbId,
// HasFile = monitored.hasFile
// });
// }
// else
// {
// // Do we need to update the availability?
// if (monitored.hasFile != existing.HasFile)
// {
// existing.HasFile = monitored.hasFile;
// }
// }
//}
using (var tran = await _ctx.Database.BeginTransactionAsync())
{
await _ctx.SonarrEpisodeCache.AddRangeAsync(episodesToAdd);
_log.LogDebug("Commiting the transaction");
await _ctx.SaveChangesAsync();
tran.Commit();
}
}
}
await OmbiQuartz.TriggerJob(nameof(IArrAvailabilityChecker), "DVR");
}
catch (Exception e)
{

@ -73,6 +73,7 @@ namespace Ombi.Schedule
{
await OmbiQuartz.Instance.AddJob<ISonarrSync>(nameof(ISonarrSync), "DVR", JobSettingsHelper.Sonarr(s));
await OmbiQuartz.Instance.AddJob<IRadarrSync>(nameof(IRadarrSync), "DVR", JobSettingsHelper.Radarr(s));
await OmbiQuartz.Instance.AddJob<IArrAvailabilityChecker>(nameof(IArrAvailabilityChecker), "DVR", null);
await OmbiQuartz.Instance.AddJob<ICouchPotatoSync>(nameof(ICouchPotatoSync), "DVR", JobSettingsHelper.CouchPotato(s));
await OmbiQuartz.Instance.AddJob<ISickRageSync>(nameof(ISickRageSync), "DVR", JobSettingsHelper.SickRageSync(s));
await OmbiQuartz.Instance.AddJob<ILidarrArtistSync>(nameof(ILidarrArtistSync), "DVR", JobSettingsHelper.LidarrArtistSync(s));

@ -29,6 +29,7 @@ export interface IUsersModel {
export interface INavBar {
icon: string;
faIcon: string;
name: string;
link: string;
requiresAdmin: boolean;
@ -36,5 +37,5 @@ export interface INavBar {
toolTip?: boolean;
toolTipMessage?: string;
style?: string;
donation?: boolean;
externalLink?: boolean;
}

@ -146,6 +146,7 @@ export interface IJobSettings {
issuesPurge: string;
retryRequests: string;
mediaDatabaseRefresh: string;
arrAvailabilityChecker: string;
}
export interface IIssueSettings extends ISettings {

@ -5,20 +5,19 @@
<span *ngFor="let nav of navItems">
<div *ngIf="(nav.requiresAdmin && isAdmin || !nav.requiresAdmin) && nav.enabled">
<a *ngIf="nav.donation" mat-list-item [href]="nav.link" target="_blank" matTooltip="{{nav.toolTipMessage | translate}}" matTooltipPosition="right" [routerLinkActive]="getTheme()">
<a *ngIf="nav.externalLink" mat-list-item [href]="nav.link" target="_blank" matTooltip="{{nav.toolTipMessage | translate}}" matTooltipPosition="right" [routerLinkActive]="getTheme()">
<mat-icon aria-label="Side nav toggle icon" [style]="nav.style" >{{nav.icon}}</mat-icon>
<mat-icon *ngIf="nav.icon" aria-label="Side nav toggle icon" [style]="nav.style" >{{nav.icon}}</mat-icon>
<i *ngIf="nav.faIcon" class="fa fa-lg {{nav.faIcon}}" style="padding-left: 5px; padding-right: 5px;" aria-hidden="true"></i>
&nbsp;{{nav.name | translate}}
</a>
<a *ngIf="!nav.donation" mat-list-item [routerLink]="nav.link" [style]="nav.color" [routerLinkActive]="getTheme()">
<a *ngIf="!nav.externalLink" mat-list-item [routerLink]="nav.link" [style]="nav.color" [routerLinkActive]="getTheme()">
<mat-icon aria-label="Side nav toggle icon">{{nav.icon}}</mat-icon>
&nbsp;{{nav.name | translate}}
</a>
</a>
</div>
</span>

@ -34,7 +34,7 @@ export class MyNavComponent implements OnInit {
}
public async ngOnInit() {
this.issuesEnabled = await this.settingsService.issueEnabled().toPromise();
console.log("issues enabled: " + this.issuesEnabled);
this.theme = this.store.get("theme");
@ -42,14 +42,15 @@ export class MyNavComponent implements OnInit {
this.store.save("theme","dark");
}
this.navItems = [
{ name: "NavigationBar.Discover", icon: "find_replace", link: "/discover", requiresAdmin: false, enabled: true },
{ name: "NavigationBar.Requests", icon: "list", link: "/requests-list", requiresAdmin: false, enabled: true },
{ name: "NavigationBar.Issues", icon: "notification_important", link: "/issues", requiresAdmin: false, enabled: this.issuesEnabled },
{ name: "NavigationBar.UserManagement", icon: "account_circle", link: "/usermanagement", requiresAdmin: true, enabled: true },
{ name: "NavigationBar.Discover", icon: "find_replace", link: "/discover", requiresAdmin: false, enabled: true, faIcon: null },
{ name: "NavigationBar.Requests", icon: "list", link: "/requests-list", requiresAdmin: false, enabled: true, faIcon: null },
{ name: "NavigationBar.Issues", icon: "notification_important", link: "/issues", requiresAdmin: false, enabled: this.issuesEnabled, faIcon: null },
{ name: "NavigationBar.UserManagement", icon: "account_circle", link: "/usermanagement", requiresAdmin: true, enabled: true, faIcon: null },
// { name: "NavigationBar.Calendar", icon: "calendar_today", link: "/calendar", requiresAdmin: false, enabled: true },
{ name: "NavigationBar.Donate", icon: "attach_money", link: "https://www.paypal.me/PlexRequestsNet", donation: true, requiresAdmin: true, enabled: true, toolTip: true, style: "color:red;", toolTipMessage: 'NavigationBar.DonateTooltip' },
{ name: "NavigationBar.Settings", icon: "settings", link: "/Settings/About", requiresAdmin: true, enabled: true },
{ name: "NavigationBar.UserPreferences", icon: "person", link: "/user-preferences", requiresAdmin: false, enabled: true },
{ name: "NavigationBar.Donate", icon: "attach_money", link: "https://www.paypal.me/PlexRequestsNet", externalLink: true, requiresAdmin: true, enabled: true, toolTip: true, style: "color:red;", toolTipMessage: 'NavigationBar.DonateTooltip', faIcon: null },
{ name: "NavigationBar.FeatureSuggestion", icon: null, link: "https://features.ombi.io/", externalLink: true, requiresAdmin: false, enabled: true, toolTip: true, toolTipMessage: 'NavigationBar.FeatureSuggestionTooltip', faIcon: "fa-lightbulb-o" },
{ name: "NavigationBar.Settings", icon: "settings", link: "/Settings/About", requiresAdmin: true, enabled: true, faIcon: null },
{ name: "NavigationBar.UserPreferences", icon: "person", link: "/user-preferences", requiresAdmin: false, enabled: true, faIcon: null },
];
}

@ -46,4 +46,8 @@ export class JobService extends ServiceHelpers {
public runNewsletter(): Observable<boolean> {
return this.http.post<boolean>(`${this.url}newsletter/`, {headers: this.headers});
}
public runArrAvailabilityChecker(): Observable<boolean> {
return this.http.post<boolean>(`${this.url}arrAvailability/`, {headers: this.headers});
}
}

@ -72,19 +72,19 @@
</div>
<div class="form-group">
<label for="embyContentSync" class="control-label">Emby Sync</label>
<label for="embyContentSync" class="control-label">Emby Sync</label>
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('embyContentSync').hasError('required')}" id="embyContentSync" name="embyContentSync" formControlName="embyContentSync">
<small *ngIf="form.get('embyContentSync').hasError('required')" class="error-text">The Emby Sync is required</small>
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('embyContentSync')?.value)">Test</button>
</div>
<div class="form-group">
<label for="userImporter" class="control-label">User Importer</label>
<label for="userImporter" class="control-label">User Importer</label>
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('userImporter').hasError('required')}" id="userImporter" name="userImporter" formControlName="userImporter">
<small *ngIf="form.get('userImporter').hasError('required')" class="error-text">The User Importer is required</small>
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('userImporter')?.value)">Test</button>
</div>
<div class="form-group">
<label for="userImporter" class="control-label">Newsletter</label>
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('newsletter').hasError('required')}" id="newsletter" name="newsletter" formControlName="newsletter">
@ -105,6 +105,14 @@
<small *ngIf="form.get('mediaDatabaseRefresh').hasError('required')" class="error-text">The Media Database Refresh is required</small>
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('mediaDatabaseRefresh')?.value)">Test</button>
</div>
<div class="form-group">
<label for="userImporter" class="control-label">Radarr/Sonarr Availability Checker</label>
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('arrAvailabilityChecker').hasError('required')}" id="arrAvailabilityChecker" name="arrAvailabilityChecker" formControlName="mediaDatabaseRefresh">
<small *ngIf="form.get('arrAvailabilityChecker').hasError('required')" class="error-text">The Radarr/Sonarr Availability Checker is required</small>
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('arrAvailabilityChecker')?.value)">Test</button>
<button type="button" class="btn btn-sm btn-primary-outline" (click)="runArrAvailabilityChecker()">Run</button>
</div>
</div>
<div class="form-group">
<div>

@ -1,7 +1,7 @@
import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { NotificationService, SettingsService } from "../../services";
import { NotificationService, SettingsService, JobService } from "../../services";
@Component({
templateUrl: "./jobs.component.html",
@ -10,13 +10,14 @@ import { NotificationService, SettingsService } from "../../services";
export class JobsComponent implements OnInit {
public form: FormGroup;
public profilesRunning: boolean;
constructor(private readonly settingsService: SettingsService,
private readonly fb: FormBuilder,
private readonly notificationService: NotificationService) { }
private readonly notificationService: NotificationService,
private readonly jobsService: JobService) { }
public ngOnInit() {
this.settingsService.getJobSettings().subscribe(x => {
this.form = this.fb.group({
@ -27,27 +28,28 @@ export class JobsComponent implements OnInit {
userImporter: [x.userImporter, Validators.required],
sonarrSync: [x.sonarrSync, Validators.required],
radarrSync: [x.radarrSync, Validators.required],
sickRageSync: [x.sickRageSync, Validators.required],
sickRageSync: [x.sickRageSync, Validators.required],
newsletter: [x.newsletter, Validators.required],
plexRecentlyAddedSync: [x.plexRecentlyAddedSync, Validators.required],
lidarrArtistSync: [x.lidarrArtistSync, Validators.required],
issuesPurge: [x.issuesPurge, Validators.required],
retryRequests: [x.retryRequests, Validators.required],
mediaDatabaseRefresh: [x.mediaDatabaseRefresh, Validators.required],
});
arrAvailabilityChecker: [x.arrAvailabilityChecker, Validators.required],
});
});
}
public testCron(expression: string) {
this.settingsService.testCron({ expression }).subscribe(x => {
if(x.success) {
this.notificationService.success("Cron is Valid");
if(x.success) {
this.notificationService.success("Cron is Valid");
} else {
this.notificationService.error(x.message);
}
});
}
public onSubmit(form: FormGroup) {
if (form.invalid) {
this.notificationService.error("Please check your entered values");
@ -62,4 +64,8 @@ export class JobsComponent implements OnInit {
}
});
}
public runArrAvailabilityChecker() {
this.jobsService.runArrAvailabilityChecker().subscribe();
}
}

@ -8,6 +8,7 @@ using Ombi.Schedule.Jobs;
using Ombi.Schedule.Jobs.Emby;
using Ombi.Schedule.Jobs.Ombi;
using Ombi.Schedule.Jobs.Plex;
using Ombi.Schedule.Jobs.Radarr;
using Quartz;
namespace Ombi.Controllers.V1
@ -134,6 +135,17 @@ namespace Ombi.Controllers.V1
return true;
}
/// <summary>
/// Runs the Arr Availability Checker
/// </summary>
/// <returns></returns>
[HttpPost("arrAvailability")]
public async Task<bool> StartArrAvailabiltityChecker()
{
await OmbiQuartz.TriggerJob(nameof(IArrAvailabilityChecker), "DVR");
return true;
}
/// <summary>
/// Runs the newsletter
/// </summary>

@ -67,7 +67,9 @@
"RecentlyAdded": "Recently Added",
"ChangeTheme": "Change Theme",
"Calendar": "Calendar",
"UserPreferences": "Preferences"
"UserPreferences": "Preferences",
"FeatureSuggestion":"Feature Suggestion",
"FeatureSuggestionTooltip":"Have a great new idea? Suggest it here!"
},
"Search": {
"Title": "Search",

Loading…
Cancel
Save