Skip to content

Commit d43a2e4

Browse files
committed
feat(availability): ✨ Add the option for the *arr to take media availability priority
#5286
1 parent 1bbfd77 commit d43a2e4

14 files changed

Lines changed: 478 additions & 18 deletions

File tree

src/Ombi.Schedule.Tests/AvailabilityCheckerTests.cs

Lines changed: 179 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
using Moq.AutoMock;
55
using NUnit.Framework;
66
using Ombi.Core;
7+
using Ombi.Core.Settings;
78
using Ombi.Hubs;
89
using Ombi.Notifications.Models;
910
using Ombi.Schedule.Jobs;
11+
using Ombi.Settings.Settings.Models.External;
1012
using Ombi.Store.Entities;
1113
using Ombi.Store.Entities.Requests;
14+
using Ombi.Store.Repository;
1215
using Ombi.Store.Repository.Requests;
1316
using Ombi.Tests;
1417
using System.Collections.Generic;
@@ -177,15 +180,190 @@ public async Task All_One_Episode_Is_Available_In_Request()
177180
_mocker.Verify<INotificationHelper>(x => x.Notify(It.Is<NotificationOptions>(x => x.NotificationType == Helpers.NotificationType.PartiallyAvailable && x.RequestId == 1)), Times.Once);
178181
});
179182
}
183+
184+
[Test]
185+
public async Task ShouldDeferToRadarr_WhenRadarrDisabled_ReturnsFalse()
186+
{
187+
var subject = CreateSubjectWithArrDependencies();
188+
_mocker.Setup<ISettingsService<RadarrSettings>, Task<RadarrSettings>>(x => x.GetSettingsAsync())
189+
.ReturnsAsync(new RadarrSettings { Enabled = false, ScanForAvailability = true, PrioritizeArrAvailability = true });
190+
191+
var result = await subject.TestShouldDeferToRadarr(123, false);
192+
193+
Assert.That(result, Is.False);
194+
}
195+
196+
[Test]
197+
public async Task ShouldDeferToRadarr_WhenScanForAvailabilityDisabled_ReturnsFalse()
198+
{
199+
var subject = CreateSubjectWithArrDependencies();
200+
_mocker.Setup<ISettingsService<RadarrSettings>, Task<RadarrSettings>>(x => x.GetSettingsAsync())
201+
.ReturnsAsync(new RadarrSettings { Enabled = true, ScanForAvailability = false, PrioritizeArrAvailability = true });
202+
203+
var result = await subject.TestShouldDeferToRadarr(123, false);
204+
205+
Assert.That(result, Is.False);
206+
}
207+
208+
[Test]
209+
public async Task ShouldDeferToRadarr_WhenPrioritizeArrAvailabilityDisabled_ReturnsFalse()
210+
{
211+
var subject = CreateSubjectWithArrDependencies();
212+
_mocker.Setup<ISettingsService<RadarrSettings>, Task<RadarrSettings>>(x => x.GetSettingsAsync())
213+
.ReturnsAsync(new RadarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = false });
214+
215+
var result = await subject.TestShouldDeferToRadarr(123, false);
216+
217+
Assert.That(result, Is.False);
218+
}
219+
220+
[Test]
221+
public async Task ShouldDeferToRadarr_WhenNotInCache_ReturnsFalse()
222+
{
223+
var subject = CreateSubjectWithArrDependencies();
224+
_mocker.Setup<ISettingsService<RadarrSettings>, Task<RadarrSettings>>(x => x.GetSettingsAsync())
225+
.ReturnsAsync(new RadarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = true });
226+
_mocker.Setup<IExternalRepository<RadarrCache>, IQueryable<RadarrCache>>(x => x.GetAll())
227+
.Returns(new List<RadarrCache>().AsQueryable().BuildMock());
228+
229+
var result = await subject.TestShouldDeferToRadarr(123, false);
230+
231+
Assert.That(result, Is.False);
232+
}
233+
234+
[Test]
235+
public async Task ShouldDeferToRadarr_WhenInCacheWithFile_ReturnsTrue()
236+
{
237+
var subject = CreateSubjectWithArrDependencies();
238+
_mocker.Setup<ISettingsService<RadarrSettings>, Task<RadarrSettings>>(x => x.GetSettingsAsync())
239+
.ReturnsAsync(new RadarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = true });
240+
_mocker.Setup<IExternalRepository<RadarrCache>, IQueryable<RadarrCache>>(x => x.GetAll())
241+
.Returns(new List<RadarrCache> { new RadarrCache { TheMovieDbId = 123, HasFile = true, HasRegular = true } }.AsQueryable().BuildMock());
242+
243+
var result = await subject.TestShouldDeferToRadarr(123, false);
244+
245+
Assert.That(result, Is.True);
246+
}
247+
248+
[Test]
249+
public async Task ShouldDeferToRadarr_4K_WhenInCacheWith4K_ReturnsTrue()
250+
{
251+
var subject = CreateSubjectWithArrDependencies();
252+
_mocker.Setup<ISettingsService<RadarrSettings>, Task<RadarrSettings>>(x => x.GetSettingsAsync())
253+
.ReturnsAsync(new RadarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = true });
254+
_mocker.Setup<IExternalRepository<RadarrCache>, IQueryable<RadarrCache>>(x => x.GetAll())
255+
.Returns(new List<RadarrCache> { new RadarrCache { TheMovieDbId = 123, HasFile = true, Has4K = true } }.AsQueryable().BuildMock());
256+
257+
var result = await subject.TestShouldDeferToRadarr(123, true);
258+
259+
Assert.That(result, Is.True);
260+
}
261+
262+
[Test]
263+
public async Task ShouldDeferToSonarr_WhenSonarrDisabled_ReturnsFalse()
264+
{
265+
var subject = CreateSubjectWithArrDependencies();
266+
_mocker.Setup<ISettingsService<SonarrSettings>, Task<SonarrSettings>>(x => x.GetSettingsAsync())
267+
.ReturnsAsync(new SonarrSettings { Enabled = false, ScanForAvailability = true, PrioritizeArrAvailability = true });
268+
269+
var result = await subject.TestShouldDeferToSonarr(456);
270+
271+
Assert.That(result, Is.False);
272+
}
273+
274+
[Test]
275+
public async Task ShouldDeferToSonarr_WhenScanForAvailabilityDisabled_ReturnsFalse()
276+
{
277+
var subject = CreateSubjectWithArrDependencies();
278+
_mocker.Setup<ISettingsService<SonarrSettings>, Task<SonarrSettings>>(x => x.GetSettingsAsync())
279+
.ReturnsAsync(new SonarrSettings { Enabled = true, ScanForAvailability = false, PrioritizeArrAvailability = true });
280+
281+
var result = await subject.TestShouldDeferToSonarr(456);
282+
283+
Assert.That(result, Is.False);
284+
}
285+
286+
[Test]
287+
public async Task ShouldDeferToSonarr_WhenPrioritizeArrAvailabilityDisabled_ReturnsFalse()
288+
{
289+
var subject = CreateSubjectWithArrDependencies();
290+
_mocker.Setup<ISettingsService<SonarrSettings>, Task<SonarrSettings>>(x => x.GetSettingsAsync())
291+
.ReturnsAsync(new SonarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = false });
292+
293+
var result = await subject.TestShouldDeferToSonarr(456);
294+
295+
Assert.That(result, Is.False);
296+
}
297+
298+
[Test]
299+
public async Task ShouldDeferToSonarr_WhenNoEpisodesInCache_ReturnsFalse()
300+
{
301+
var subject = CreateSubjectWithArrDependencies();
302+
_mocker.Setup<ISettingsService<SonarrSettings>, Task<SonarrSettings>>(x => x.GetSettingsAsync())
303+
.ReturnsAsync(new SonarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = true });
304+
_mocker.Setup<IExternalRepository<SonarrEpisodeCache>, IQueryable<SonarrEpisodeCache>>(x => x.GetAll())
305+
.Returns(new List<SonarrEpisodeCache>().AsQueryable().BuildMock());
306+
307+
var result = await subject.TestShouldDeferToSonarr(456);
308+
309+
Assert.That(result, Is.False);
310+
}
311+
312+
[Test]
313+
public async Task ShouldDeferToSonarr_WhenEpisodesInCacheWithFile_ReturnsTrue()
314+
{
315+
var subject = CreateSubjectWithArrDependencies();
316+
_mocker.Setup<ISettingsService<SonarrSettings>, Task<SonarrSettings>>(x => x.GetSettingsAsync())
317+
.ReturnsAsync(new SonarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = true });
318+
_mocker.Setup<IExternalRepository<SonarrEpisodeCache>, IQueryable<SonarrEpisodeCache>>(x => x.GetAll())
319+
.Returns(new List<SonarrEpisodeCache> { new SonarrEpisodeCache { TvDbId = 456, HasFile = true } }.AsQueryable().BuildMock());
320+
321+
var result = await subject.TestShouldDeferToSonarr(456);
322+
323+
Assert.That(result, Is.True);
324+
}
325+
326+
private TestAvailabilityCheckerWithArrSupport CreateSubjectWithArrDependencies()
327+
{
328+
_mocker.Setup<ISettingsService<RadarrSettings>, Task<RadarrSettings>>(x => x.GetSettingsAsync())
329+
.ReturnsAsync(new RadarrSettings());
330+
_mocker.Setup<ISettingsService<SonarrSettings>, Task<SonarrSettings>>(x => x.GetSettingsAsync())
331+
.ReturnsAsync(new SonarrSettings());
332+
_mocker.Setup<IExternalRepository<RadarrCache>, IQueryable<RadarrCache>>(x => x.GetAll())
333+
.Returns(new List<RadarrCache>().AsQueryable().BuildMock());
334+
_mocker.Setup<IExternalRepository<SonarrEpisodeCache>, IQueryable<SonarrEpisodeCache>>(x => x.GetAll())
335+
.Returns(new List<SonarrEpisodeCache>().AsQueryable().BuildMock());
336+
337+
return _mocker.CreateInstance<TestAvailabilityCheckerWithArrSupport>();
338+
}
180339
}
181340

182341

183342
public class TestAvailabilityChecker : AvailabilityChecker
184343
{
185-
public TestAvailabilityChecker(ITvRequestRepository tvRequest, INotificationHelper notification, ILogger log, INotificationHubService notificationHubService) : base(tvRequest, notification, log, notificationHubService)
344+
public TestAvailabilityChecker(ITvRequestRepository tvRequest, INotificationHelper notification, ILogger log, INotificationHubService notificationHubService) : base(tvRequest, notification, log, notificationHubService, null, null, null, null)
186345
{
187346
}
188347

189348
public new Task ProcessTvShow(IQueryable<IBaseMediaServerEpisode> seriesEpisodes, ChildRequests child) => base.ProcessTvShow(seriesEpisodes, child);
190349
}
350+
351+
public class TestAvailabilityCheckerWithArrSupport : AvailabilityChecker
352+
{
353+
public TestAvailabilityCheckerWithArrSupport(
354+
ITvRequestRepository tvRequest,
355+
INotificationHelper notification,
356+
ILogger log,
357+
INotificationHubService notificationHubService,
358+
ISettingsService<RadarrSettings> radarrSettings,
359+
ISettingsService<SonarrSettings> sonarrSettings,
360+
IExternalRepository<RadarrCache> radarrCache,
361+
IExternalRepository<SonarrEpisodeCache> sonarrEpisodeCache)
362+
: base(tvRequest, notification, log, notificationHubService, radarrSettings, sonarrSettings, radarrCache, sonarrEpisodeCache)
363+
{
364+
}
365+
366+
public Task<bool> TestShouldDeferToRadarr(int theMovieDbId, bool is4K) => base.ShouldDeferToRadarr(theMovieDbId, is4K);
367+
public Task<bool> TestShouldDeferToSonarr(int tvDbId) => base.ShouldDeferToSonarr(tvDbId);
368+
}
191369
}

src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
using Ombi.Tests;
1818
using Moq.AutoMock;
1919
using Ombi.Settings.Settings.Models;
20+
using Ombi.Settings.Settings.Models.External;
21+
using Ombi.Core.Settings;
2022
using Ombi.Notifications.Models;
2123

2224
namespace Ombi.Schedule.Tests
@@ -241,6 +243,126 @@ public async Task ProcessTv_ShouldMark_Episode_Available_By_TitleMatch()
241243
Assert.True(request.SeasonRequests[0].Episodes[0].Available);
242244
}
243245

246+
[Test]
247+
public async Task ProcessMovies_ShouldNotMarkAvailable_WhenRadarrPriorityEnabled_AndInRadarrCache()
248+
{
249+
// Setup Radarr priority
250+
_mocker.Setup<ISettingsService<RadarrSettings>, Task<RadarrSettings>>(x => x.GetSettingsAsync())
251+
.ReturnsAsync(new RadarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = true });
252+
_mocker.Setup<IExternalRepository<RadarrCache>, IQueryable<RadarrCache>>(x => x.GetAll())
253+
.Returns(new List<RadarrCache> { new RadarrCache { TheMovieDbId = 123, HasFile = true, HasRegular = true } }.AsQueryable().BuildMock());
254+
255+
var request = new MovieRequests
256+
{
257+
ImdbId = "test",
258+
TheMovieDbId = 123
259+
};
260+
_mocker.Setup<IMovieRequestRepository, IQueryable<MovieRequests>>(x => x.GetAll()).Returns(new List<MovieRequests> { request }.AsQueryable());
261+
_mocker.Setup<IPlexContentRepository, Task<PlexServerContent>>(x => x.Get("test", ProviderType.ImdbId)).ReturnsAsync(new PlexServerContent());
262+
263+
await _subject.Execute(null);
264+
265+
// Should NOT mark as available because Radarr has priority
266+
Assert.Multiple(() =>
267+
{
268+
Assert.That(request.Available, Is.False);
269+
Assert.That(request.MarkedAsAvailable, Is.Null);
270+
});
271+
272+
_mocker.Verify<IMovieRequestRepository>(x => x.SaveChangesAsync(), Times.Never);
273+
_mocker.Verify<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()), Times.Never);
274+
}
275+
276+
[Test]
277+
public async Task ProcessMovies_ShouldMarkAvailable_WhenRadarrPriorityDisabled_AndInPlex()
278+
{
279+
// Setup Radarr priority disabled
280+
_mocker.Setup<ISettingsService<RadarrSettings>, Task<RadarrSettings>>(x => x.GetSettingsAsync())
281+
.ReturnsAsync(new RadarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = false });
282+
283+
var request = new MovieRequests
284+
{
285+
ImdbId = "test"
286+
};
287+
_mocker.Setup<IMovieRequestRepository, IQueryable<MovieRequests>>(x => x.GetAll()).Returns(new List<MovieRequests> { request }.AsQueryable());
288+
_mocker.Setup<IPlexContentRepository, Task<PlexServerContent>>(x => x.Get("test", ProviderType.ImdbId)).ReturnsAsync(new PlexServerContent());
289+
290+
await _subject.Execute(null);
291+
292+
// Should mark as available because priority is disabled
293+
Assert.Multiple(() =>
294+
{
295+
Assert.That(request.Available, Is.True);
296+
Assert.That(request.MarkedAsAvailable, Is.Not.Null);
297+
});
298+
299+
_mocker.Verify<IMovieRequestRepository>(x => x.SaveChangesAsync(), Times.Once);
300+
_mocker.Verify<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()), Times.Once);
301+
}
302+
303+
[Test]
304+
public async Task ProcessMovies_ShouldMarkAvailable_WhenRadarrPriorityEnabled_ButNotInRadarrCache()
305+
{
306+
// Setup Radarr priority enabled but content not in cache
307+
_mocker.Setup<ISettingsService<RadarrSettings>, Task<RadarrSettings>>(x => x.GetSettingsAsync())
308+
.ReturnsAsync(new RadarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = true });
309+
_mocker.Setup<IExternalRepository<RadarrCache>, IQueryable<RadarrCache>>(x => x.GetAll())
310+
.Returns(new List<RadarrCache>().AsQueryable().BuildMock());
311+
312+
var request = new MovieRequests
313+
{
314+
ImdbId = "test",
315+
TheMovieDbId = 123
316+
};
317+
_mocker.Setup<IMovieRequestRepository, IQueryable<MovieRequests>>(x => x.GetAll()).Returns(new List<MovieRequests> { request }.AsQueryable());
318+
_mocker.Setup<IPlexContentRepository, Task<PlexServerContent>>(x => x.Get("test", ProviderType.ImdbId)).ReturnsAsync(new PlexServerContent());
319+
320+
await _subject.Execute(null);
321+
322+
// Should mark as available because content is not in Radarr cache
323+
Assert.Multiple(() =>
324+
{
325+
Assert.That(request.Available, Is.True);
326+
Assert.That(request.MarkedAsAvailable, Is.Not.Null);
327+
});
328+
329+
_mocker.Verify<IMovieRequestRepository>(x => x.SaveChangesAsync(), Times.Once);
330+
_mocker.Verify<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()), Times.Once);
331+
}
332+
333+
[Test]
334+
public async Task ProcessTv_ShouldNotMarkAvailable_WhenSonarrPriorityEnabled_AndInSonarrCache()
335+
{
336+
// Setup Sonarr priority
337+
_mocker.Setup<ISettingsService<SonarrSettings>, Task<SonarrSettings>>(x => x.GetSettingsAsync())
338+
.ReturnsAsync(new SonarrSettings { Enabled = true, ScanForAvailability = true, PrioritizeArrAvailability = true });
339+
_mocker.Setup<IExternalRepository<SonarrEpisodeCache>, IQueryable<SonarrEpisodeCache>>(x => x.GetAll())
340+
.Returns(new List<SonarrEpisodeCache> { new SonarrEpisodeCache { TvDbId = 99, HasFile = true } }.AsQueryable().BuildMock());
341+
342+
var request = CreateChildRequest(null, 33, 99);
343+
_mocker.Setup<ITvRequestRepository, IQueryable<ChildRequests>>(x => x.GetChild()).Returns(new List<ChildRequests> { request }.AsQueryable().BuildMock());
344+
_mocker.Setup<IPlexContentRepository, IQueryable<IMediaServerEpisode>>(x => x.GetAllEpisodes()).Returns(new List<PlexEpisode>
345+
{
346+
new PlexEpisode
347+
{
348+
Series = new PlexServerContent
349+
{
350+
TheMovieDbId = 33.ToString(),
351+
Title = "abc"
352+
},
353+
EpisodeNumber = 1,
354+
SeasonNumber = 2,
355+
}
356+
}.AsQueryable().BuildMock());
357+
358+
await _subject.Execute(null);
359+
360+
// Should NOT mark as available because Sonarr has priority
361+
Assert.That(request.SeasonRequests[0].Episodes[0].Available, Is.False);
362+
// Save is called at the end of ProcessTv, but episodes should not be marked available
363+
_mocker.Verify<ITvRequestRepository>(x => x.Save(), Times.Once);
364+
}
365+
244366
private ChildRequests CreateChildRequest(string imdbId, int theMovieDbId, int tvdbId)
245367
{
246368
return new ChildRequests

0 commit comments

Comments
 (0)