diff --git a/frontend/src/Helpers/Props/icons.js b/frontend/src/Helpers/Props/icons.js
index fa7eb85c7..25e4c4627 100644
--- a/frontend/src/Helpers/Props/icons.js
+++ b/frontend/src/Helpers/Props/icons.js
@@ -190,6 +190,7 @@ export const SCORE = fasUserPlus;
export const SEARCH = fasSearch;
export const MOVIE_CONTINUING = fasPlay;
export const SERIES_ENDED = fasStop;
+export const MOVIE_DELETED = fasExclamationTriangle;
export const SETTINGS = fasCogs;
export const SHUTDOWN = fasPowerOff;
export const SORT = fasSort;
diff --git a/frontend/src/Movie/Index/Table/MovieIndexRow.js b/frontend/src/Movie/Index/Table/MovieIndexRow.js
index 933d65302..5ecf54ebc 100644
--- a/frontend/src/Movie/Index/Table/MovieIndexRow.js
+++ b/frontend/src/Movie/Index/Table/MovieIndexRow.js
@@ -13,7 +13,7 @@ import MovieTitleLink from 'Movie/MovieTitleLink';
import EditMovieModalConnector from 'Movie/Edit/EditMovieModalConnector';
import DeleteMovieModal from 'Movie/Delete/DeleteMovieModal';
import MovieStatusCell from './MovieStatusCell';
-import MovieStatusConnector from 'Movie/MovieStatusConnector';
+import MovieFileStatusConnector from 'Movie/MovieFileStatusConnector';
import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
import styles from './MovieIndexRow.css';
@@ -278,7 +278,7 @@ class MovieIndexRow extends Component {
key={name}
className={styles[name]}
>
-
diff --git a/frontend/src/Movie/Index/Table/MovieStatusCell.js b/frontend/src/Movie/Index/Table/MovieStatusCell.js
index 7865c7d4f..ee5a2b9b0 100644
--- a/frontend/src/Movie/Index/Table/MovieStatusCell.js
+++ b/frontend/src/Movie/Index/Table/MovieStatusCell.js
@@ -3,6 +3,7 @@ import React from 'react';
import { icons } from 'Helpers/Props';
import Icon from 'Components/Icon';
import VirtualTableRowCell from 'Components/Table/Cells/TableRowCell';
+import { getMovieStatusDetails } from 'Movie/MovieStatus';
import styles from './MovieStatusCell.css';
function MovieStatusCell(props) {
@@ -14,6 +15,8 @@ function MovieStatusCell(props) {
...otherProps
} = props;
+ const statusDetails = getMovieStatusDetails(status);
+
return (
- {
- status === 'announced' ?
- : null
- }
-
- {
- status === 'inCinemas' ?
- : null
- }
+
- {
- status === 'released' ?
- : null
- }
);
}
diff --git a/frontend/src/Movie/MovieStatus.css b/frontend/src/Movie/MovieFileStatus.css
similarity index 100%
rename from frontend/src/Movie/MovieStatus.css
rename to frontend/src/Movie/MovieFileStatus.css
diff --git a/frontend/src/Movie/MovieFileStatus.js b/frontend/src/Movie/MovieFileStatus.js
new file mode 100644
index 000000000..310c77cb6
--- /dev/null
+++ b/frontend/src/Movie/MovieFileStatus.js
@@ -0,0 +1,124 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { icons, kinds, sizes } from 'Helpers/Props';
+import Icon from 'Components/Icon';
+import ProgressBar from 'Components/ProgressBar';
+import QueueDetails from 'Activity/Queue/QueueDetails';
+import MovieQuality from 'Movie/MovieQuality';
+import Label from 'Components/Label';
+import styles from './MovieFileStatus.css';
+
+function MovieFileStatus(props) {
+ const {
+ inCinemas,
+ isAvailable,
+ monitored,
+ grabbed,
+ queueItem,
+ movieFile
+ } = props;
+
+ const hasMovieFile = !!movieFile;
+ const isQueued = !!queueItem;
+ const hasReleased = isAvailable && inCinemas;
+
+ if (isQueued) {
+ const {
+ sizeleft,
+ size
+ } = queueItem;
+
+ const progress = (100 - sizeleft / size * 100);
+
+ return (
+
+
+ }
+ />
+
+ );
+ }
+
+ if (grabbed) {
+ return (
+
+
+
+ );
+ }
+
+ if (hasMovieFile) {
+ const quality = movieFile.quality;
+
+ return (
+
+
+
+ );
+ }
+
+ if (!monitored) {
+ return (
+
+
+
+ );
+ }
+
+ if (hasReleased) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ );
+}
+
+MovieFileStatus.propTypes = {
+ inCinemas: PropTypes.string,
+ isAvailable: PropTypes.bool,
+ monitored: PropTypes.bool.isRequired,
+ grabbed: PropTypes.bool,
+ queueItem: PropTypes.object,
+ movieFile: PropTypes.object
+};
+
+export default MovieFileStatus;
diff --git a/frontend/src/Movie/MovieStatusConnector.js b/frontend/src/Movie/MovieFileStatusConnector.js
similarity index 83%
rename from frontend/src/Movie/MovieStatusConnector.js
rename to frontend/src/Movie/MovieFileStatusConnector.js
index c3d721faa..4812f94b4 100644
--- a/frontend/src/Movie/MovieStatusConnector.js
+++ b/frontend/src/Movie/MovieFileStatusConnector.js
@@ -5,7 +5,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createMovieSelector from 'Store/Selectors/createMovieSelector';
import createQueueItemSelector from 'Store/Selectors/createQueueItemSelector';
-import MovieStatus from './MovieStatus';
+import MovieFileStatus from './MovieFileStatus';
function createMapStateToProps() {
return createSelector(
@@ -30,22 +30,22 @@ function createMapStateToProps() {
const mapDispatchToProps = {
};
-class MovieStatusConnector extends Component {
+class MovieFileStatusConnector extends Component {
//
// Render
render() {
return (
-
);
}
}
-MovieStatusConnector.propTypes = {
+MovieFileStatusConnector.propTypes = {
movieId: PropTypes.number.isRequired
};
-export default connect(createMapStateToProps, mapDispatchToProps)(MovieStatusConnector);
+export default connect(createMapStateToProps, mapDispatchToProps)(MovieFileStatusConnector);
diff --git a/frontend/src/Movie/MovieStatus.js b/frontend/src/Movie/MovieStatus.js
index c7dfc39ab..3df5f695a 100644
--- a/frontend/src/Movie/MovieStatus.js
+++ b/frontend/src/Movie/MovieStatus.js
@@ -1,124 +1,32 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import { icons, kinds, sizes } from 'Helpers/Props';
-import Icon from 'Components/Icon';
-import ProgressBar from 'Components/ProgressBar';
-import QueueDetails from 'Activity/Queue/QueueDetails';
-import MovieQuality from 'Movie/MovieQuality';
-import Label from 'Components/Label';
-import styles from './MovieStatus.css';
-
-function MovieStatus(props) {
- const {
- inCinemas,
- isAvailable,
- monitored,
- grabbed,
- queueItem,
- movieFile
- } = props;
-
- const hasMovieFile = !!movieFile;
- const isQueued = !!queueItem;
- const hasReleased = isAvailable && inCinemas;
-
- if (isQueued) {
- const {
- sizeleft,
- size
- } = queueItem;
-
- const progress = (100 - sizeleft / size * 100);
-
- return (
-
-
- }
- />
-
- );
- }
-
- if (grabbed) {
- return (
-
-
-
- );
- }
-
- if (hasMovieFile) {
- const quality = movieFile.quality;
-
- return (
-
-
-
- );
- }
-
- if (!monitored) {
- return (
-
-
-
- );
+import { icons } from 'Helpers/Props';
+
+export function getMovieStatusDetails(status) {
+
+ let statusDetails = {
+ icon: icons.ANNOUNCED,
+ title: 'Announced',
+ message: 'Movie is announced'
+ };
+
+ if (status === 'deleted') {
+ statusDetails = {
+ icon: icons.MOVIE_DELETED,
+ title: 'Deleted',
+ message: 'Movie was deleted from TMDb'
+ };
+ } else if (status === 'inCinemas') {
+ statusDetails = {
+ icon: icons.IN_CINEMAS,
+ title: 'In Cinemas',
+ message: 'Movie is in Cinemas'
+ };
+ } else if (status === 'released') {
+ statusDetails = {
+ icon: icons.MOVIE_FILE,
+ title: 'Released',
+ message: 'Movie is released'
+ };
}
- if (hasReleased) {
- return (
-
-
-
- );
- }
-
- return (
-
-
-
- );
+ return statusDetails;
}
-
-MovieStatus.propTypes = {
- inCinemas: PropTypes.string,
- isAvailable: PropTypes.bool,
- monitored: PropTypes.bool.isRequired,
- grabbed: PropTypes.bool,
- queueItem: PropTypes.object,
- movieFile: PropTypes.object
-};
-
-export default MovieStatus;
diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/RemovedMovieCheckFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/RemovedMovieCheckFixture.cs
new file mode 100644
index 000000000..887f3d99b
--- /dev/null
+++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/RemovedMovieCheckFixture.cs
@@ -0,0 +1,75 @@
+using System.Collections.Generic;
+using FizzWare.NBuilder;
+using NUnit.Framework;
+using NzbDrone.Core.HealthCheck.Checks;
+using NzbDrone.Core.Movies;
+using NzbDrone.Core.Test.Framework;
+
+namespace NzbDrone.Core.Test.HealthCheck.Checks
+{
+ [TestFixture]
+ public class RemovedSeriesCheckFixture : CoreTest
+ {
+ private void GivenMovie(int amount, int deleted)
+ {
+ List movie;
+
+ if (amount == 0)
+ {
+ movie = new List();
+ }
+ else if (deleted == 0)
+ {
+ movie = Builder.CreateListOfSize(amount)
+ .All()
+ .With(v => v.Status = MovieStatusType.Released)
+ .BuildList();
+ }
+ else
+ {
+ movie = Builder.CreateListOfSize(amount)
+ .All()
+ .With(v => v.Status = MovieStatusType.Released)
+ .Random(deleted)
+ .With(v => v.Status = MovieStatusType.Deleted)
+ .BuildList();
+ }
+
+ Mocker.GetMock()
+ .Setup(v => v.GetAllMovies())
+ .Returns(movie);
+ }
+
+ [Test]
+ public void should_return_error_if_movie_no_longer_on_tmdb()
+ {
+ GivenMovie(4, 1);
+
+ Subject.Check().ShouldBeError();
+ }
+
+ [Test]
+ public void should_return_error_if_multiple_movie_no_longer_on_tmdb()
+ {
+ GivenMovie(4, 2);
+
+ Subject.Check().ShouldBeError();
+ }
+
+ [Test]
+ public void should_return_ok_if_all_movie_still_on_tmdb()
+ {
+ GivenMovie(4, 0);
+
+ Subject.Check().ShouldBeOk();
+ }
+
+ [Test]
+ public void should_return_ok_if_no_movie_exist()
+ {
+ GivenMovie(0, 0);
+
+ Subject.Check().ShouldBeOk();
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/HealthCheck/HealthCheckServiceFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/HealthCheckServiceFixture.cs
new file mode 100644
index 000000000..fcf9d58c8
--- /dev/null
+++ b/src/NzbDrone.Core.Test/HealthCheck/HealthCheckServiceFixture.cs
@@ -0,0 +1,81 @@
+using System.Collections.Generic;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Common.Cache;
+using NzbDrone.Common.Messaging;
+using NzbDrone.Core.HealthCheck;
+using NzbDrone.Core.Test.Framework;
+
+namespace NzbDrone.Core.Test.HealthCheck
+{
+ public class HealthCheckServiceFixture : CoreTest
+ {
+ private FakeHealthCheck _healthCheck;
+
+ [SetUp]
+ public void SetUp()
+ {
+ _healthCheck = new FakeHealthCheck();
+
+ Mocker.SetConstant>(new[] { _healthCheck });
+ Mocker.SetConstant(Mocker.Resolve());
+ }
+
+ [Test]
+ public void should_not_execute_conditional()
+ {
+ Subject.HandleAsync(new FakeEvent());
+
+ _healthCheck.Executed.Should().BeFalse();
+ }
+
+ [Test]
+ public void should_execute_conditional()
+ {
+ Subject.HandleAsync(new FakeEvent() { ShouldExecute = true });
+
+ _healthCheck.Executed.Should().BeTrue();
+ }
+
+ [Test]
+ public void should_execute_unconditional()
+ {
+ Subject.HandleAsync(new FakeEvent2());
+
+ _healthCheck.Executed.Should().BeTrue();
+ }
+ }
+
+ public class FakeEvent : IEvent
+ {
+ public bool ShouldExecute { get; set; }
+ }
+
+ public class FakeEvent2 : IEvent
+ {
+ public bool ShouldExecute { get; set; }
+ }
+
+ [CheckOn(typeof(FakeEvent))]
+ [CheckOn(typeof(FakeEvent2))]
+ public class FakeHealthCheck : IProvideHealthCheck, ICheckOnCondition
+ {
+ public bool CheckOnStartup => false;
+ public bool CheckOnSchedule => false;
+
+ public bool Executed { get; set; }
+ public bool Checked { get; set; }
+
+ public Core.HealthCheck.HealthCheck Check()
+ {
+ Executed = true;
+
+ return new Core.HealthCheck.HealthCheck(GetType());
+ }
+
+ public bool ShouldCheckOnEvent(FakeEvent message)
+ {
+ return message.ShouldExecute;
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/MovieTests/RefreshMovieServiceFixture.cs b/src/NzbDrone.Core.Test/MovieTests/RefreshMovieServiceFixture.cs
index c5cc267e8..adcd01c3f 100644
--- a/src/NzbDrone.Core.Test/MovieTests/RefreshMovieServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/MovieTests/RefreshMovieServiceFixture.cs
@@ -1,3 +1,6 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
@@ -6,6 +9,7 @@ using NzbDrone.Core.Exceptions;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Commands;
+using NzbDrone.Core.Movies.Credits;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
@@ -13,7 +17,6 @@ using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MovieTests
{
[TestFixture]
- [Ignore("Weird moq errors")]
public class RefreshMovieServiceFixture : CoreTest
{
private Movie _movie;
@@ -22,36 +25,37 @@ namespace NzbDrone.Core.Test.MovieTests
public void Setup()
{
_movie = Builder.CreateNew()
- .Build();
+ .With(s => s.Status = MovieStatusType.Released)
+ .Build();
Mocker.GetMock()
.Setup(s => s.GetMovie(_movie.Id))
.Returns(_movie);
Mocker.GetMock()
- .Setup(s => s.GetMovieInfo(It.IsAny(), It.IsAny(), false))
- .Callback(p => { throw new MovieNotFoundException(p.ToString()); });
+ .Setup(s => s.GetMovieInfo(It.IsAny(), It.IsAny(), It.IsAny()))
+ .Callback((i, p, b) => { throw new MovieNotFoundException(i); });
}
private void GivenNewMovieInfo(Movie movie)
{
Mocker.GetMock()
- .Setup(s => s.GetMovieInfo(_movie.ImdbId))
- .Returns(movie);
+ .Setup(s => s.GetMovieInfo(_movie.TmdbId, It.IsAny(), It.IsAny()))
+ .Returns(new Tuple>(movie, new List()));
}
[Test]
- public void should_update_tvrage_id_if_changed()
+ public void should_update_imdb_id_if_changed()
{
- var newSeriesInfo = _movie.JsonClone();
- newSeriesInfo.ImdbId = _movie.ImdbId + 1;
+ var newMovieInfo = _movie.JsonClone();
+ newMovieInfo.ImdbId = _movie.ImdbId + 1;
- GivenNewMovieInfo(newSeriesInfo);
+ GivenNewMovieInfo(newMovieInfo);
Subject.Execute(new RefreshMovieCommand(_movie.Id));
Mocker.GetMock()
- .Verify(v => v.UpdateMovie(It.Is(s => s.ImdbId == newSeriesInfo.ImdbId)));
+ .Verify(v => v.UpdateMovie(It.Is>(s => s.First().ImdbId == newMovieInfo.ImdbId), true));
}
[Test]
@@ -60,7 +64,7 @@ namespace NzbDrone.Core.Test.MovieTests
Subject.Execute(new RefreshMovieCommand(_movie.Id));
Mocker.GetMock()
- .Verify(v => v.UpdateMovie(It.IsAny()), Times.Never());
+ .Verify(v => v.UpdateMovie(It.Is(s => s.Status == MovieStatusType.Deleted)), Times.Once());
ExceptionVerification.ExpectedErrors(1);
}
@@ -68,17 +72,41 @@ namespace NzbDrone.Core.Test.MovieTests
[Test]
public void should_update_if_tmdb_id_changed()
{
- var newSeriesInfo = _movie.JsonClone();
- newSeriesInfo.TmdbId = _movie.TmdbId + 1;
+ var newMovieInfo = _movie.JsonClone();
+ newMovieInfo.TmdbId = _movie.TmdbId + 1;
- GivenNewMovieInfo(newSeriesInfo);
+ GivenNewMovieInfo(newMovieInfo);
Subject.Execute(new RefreshMovieCommand(_movie.Id));
Mocker.GetMock()
- .Verify(v => v.UpdateMovie(It.Is(s => s.TmdbId == newSeriesInfo.TmdbId)));
+ .Verify(v => v.UpdateMovie(It.Is>(s => s.First().TmdbId == newMovieInfo.TmdbId), true));
ExceptionVerification.ExpectedWarns(1);
}
+
+ [Test]
+ public void should_mark_as_deleted_if_tmdb_id_not_found()
+ {
+ Subject.Execute(new RefreshMovieCommand(_movie.Id));
+
+ Mocker.GetMock()
+ .Verify(v => v.UpdateMovie(It.Is(s => s.Status == MovieStatusType.Deleted)), Times.Once());
+
+ ExceptionVerification.ExpectedErrors(1);
+ }
+
+ [Test]
+ public void should_not_remark_as_deleted_if_tmdb_id_not_found()
+ {
+ _movie.Status = MovieStatusType.Deleted;
+
+ Subject.Execute(new RefreshMovieCommand(_movie.Id));
+
+ Mocker.GetMock()
+ .Verify(v => v.UpdateMovie(It.IsAny()), Times.Never());
+
+ ExceptionVerification.ExpectedErrors(1);
+ }
}
}
diff --git a/src/NzbDrone.Core/Exceptions/MovieNotFoundExceptions.cs b/src/NzbDrone.Core/Exceptions/MovieNotFoundExceptions.cs
index c2345bd93..5018b2889 100644
--- a/src/NzbDrone.Core/Exceptions/MovieNotFoundExceptions.cs
+++ b/src/NzbDrone.Core/Exceptions/MovieNotFoundExceptions.cs
@@ -1,27 +1,33 @@
-using NzbDrone.Common.Exceptions;
+using NzbDrone.Common.Exceptions;
namespace NzbDrone.Core.Exceptions
{
public class MovieNotFoundException : NzbDroneException
{
- public string ImdbId { get; set; }
+ public int TmdbMovieId { get; set; }
- public MovieNotFoundException(string imdbid)
- : base(string.Format("Movie with imdbid {0} was not found, it may have been removed from IMDb.", imdbid))
+ public MovieNotFoundException(int tmdbMovieId)
+ : base(string.Format("Movie with tmdbId {0} was not found, it may have been removed from TMDb.", tmdbMovieId))
{
- ImdbId = imdbid;
+ TmdbMovieId = tmdbMovieId;
}
- public MovieNotFoundException(string imdbid, string message, params object[] args)
+ public MovieNotFoundException(string imdbId)
+ : base(string.Format("Movie with IMDBId {0} was not found, it may have been removed from TMDb.", imdbId))
+ {
+ TmdbMovieId = 0;
+ }
+
+ public MovieNotFoundException(int tmdbMovieId, string message, params object[] args)
: base(message, args)
{
- ImdbId = imdbid;
+ TmdbMovieId = tmdbMovieId;
}
- public MovieNotFoundException(string imdbid, string message)
+ public MovieNotFoundException(int tmdbMovieId, string message)
: base(message)
{
- ImdbId = imdbid;
+ TmdbMovieId = tmdbMovieId;
}
}
}
diff --git a/src/NzbDrone.Core/HealthCheck/Checks/RemovedMovieCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/RemovedMovieCheck.cs
new file mode 100644
index 000000000..fdf31a8e5
--- /dev/null
+++ b/src/NzbDrone.Core/HealthCheck/Checks/RemovedMovieCheck.cs
@@ -0,0 +1,48 @@
+using System.Linq;
+using NzbDrone.Common.Extensions;
+using NzbDrone.Core.Movies;
+using NzbDrone.Core.Movies.Events;
+
+namespace NzbDrone.Core.HealthCheck.Checks
+{
+ [CheckOn(typeof(MovieUpdatedEvent))]
+ [CheckOn(typeof(MovieDeletedEvent), CheckOnCondition.FailedOnly)]
+ public class RemovedSeriesCheck : HealthCheckBase, ICheckOnCondition, ICheckOnCondition
+ {
+ private readonly IMovieService _movieService;
+
+ public RemovedSeriesCheck(IMovieService movieService)
+ {
+ _movieService = movieService;
+ }
+
+ public override HealthCheck Check()
+ {
+ var deletedMovie = _movieService.GetAllMovies().Where(v => v.Status == MovieStatusType.Deleted).ToList();
+
+ if (deletedMovie.Empty())
+ {
+ return new HealthCheck(GetType());
+ }
+
+ var movieText = deletedMovie.Select(s => $"{s.Title} (tmdbid {s.TmdbId})").Join(", ");
+
+ if (deletedMovie.Count == 1)
+ {
+ return new HealthCheck(GetType(), HealthCheckResult.Error, $"Movie {movieText} was removed from TMDb");
+ }
+
+ return new HealthCheck(GetType(), HealthCheckResult.Error, $"Movie {movieText} were removed from TMDb");
+ }
+
+ public bool ShouldCheckOnEvent(MovieDeletedEvent message)
+ {
+ return message.Movie.Status == MovieStatusType.Deleted;
+ }
+
+ public bool ShouldCheckOnEvent(MovieUpdatedEvent message)
+ {
+ return message.Movie.Status == MovieStatusType.Deleted;
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/HealthCheck/EventDrivenHealthCheck.cs b/src/NzbDrone.Core/HealthCheck/EventDrivenHealthCheck.cs
index 0b55c1ff2..32dc48bff 100644
--- a/src/NzbDrone.Core/HealthCheck/EventDrivenHealthCheck.cs
+++ b/src/NzbDrone.Core/HealthCheck/EventDrivenHealthCheck.cs
@@ -1,14 +1,45 @@
+using NzbDrone.Common.Messaging;
+
namespace NzbDrone.Core.HealthCheck
{
- public class EventDrivenHealthCheck
+ public interface IEventDrivenHealthCheck
+ {
+ IProvideHealthCheck HealthCheck { get; }
+
+ bool ShouldExecute(IEvent message, bool previouslyFailed);
+ }
+
+ public class EventDrivenHealthCheck : IEventDrivenHealthCheck
{
public IProvideHealthCheck HealthCheck { get; set; }
public CheckOnCondition Condition { get; set; }
+ public ICheckOnCondition EventFilter { get; set; }
public EventDrivenHealthCheck(IProvideHealthCheck healthCheck, CheckOnCondition condition)
{
HealthCheck = healthCheck;
Condition = condition;
+ EventFilter = healthCheck as ICheckOnCondition;
+ }
+
+ public bool ShouldExecute(IEvent message, bool previouslyFailed)
+ {
+ if (Condition == CheckOnCondition.SuccessfulOnly && previouslyFailed)
+ {
+ return false;
+ }
+
+ if (Condition == CheckOnCondition.FailedOnly && !previouslyFailed)
+ {
+ return false;
+ }
+
+ if (EventFilter != null && !EventFilter.ShouldCheckOnEvent((TEvent)message))
+ {
+ return false;
+ }
+
+ return true;
}
}
}
diff --git a/src/NzbDrone.Core/HealthCheck/HealthCheckService.cs b/src/NzbDrone.Core/HealthCheck/HealthCheckService.cs
index 761e9f7d0..24b112207 100644
--- a/src/NzbDrone.Core/HealthCheck/HealthCheckService.cs
+++ b/src/NzbDrone.Core/HealthCheck/HealthCheckService.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Cache;
-using NzbDrone.Common.Extensions;
using NzbDrone.Common.Messaging;
using NzbDrone.Common.Reflection;
using NzbDrone.Core.Lifecycle;
@@ -25,7 +24,7 @@ namespace NzbDrone.Core.HealthCheck
private readonly IProvideHealthCheck[] _healthChecks;
private readonly IProvideHealthCheck[] _startupHealthChecks;
private readonly IProvideHealthCheck[] _scheduledHealthChecks;
- private readonly Dictionary _eventDrivenHealthChecks;
+ private readonly Dictionary _eventDrivenHealthChecks;
private readonly IEventAggregator _eventAggregator;
private readonly ICacheManager _cacheManager;
private readonly Logger _logger;
@@ -54,10 +53,16 @@ namespace NzbDrone.Core.HealthCheck
return _healthCheckResults.Values.ToList();
}
- private Dictionary GetEventDrivenHealthChecks()
+ private Dictionary GetEventDrivenHealthChecks()
{
return _healthChecks
- .SelectMany(h => h.GetType().GetAttributes().Select(a => Tuple.Create(a.EventType, new EventDrivenHealthCheck(h, a.Condition))))
+ .SelectMany(h => h.GetType().GetAttributes().Select(a =>
+ {
+ var eventDrivenType = typeof(EventDrivenHealthCheck<>).MakeGenericType(a.EventType);
+ var eventDriven = (IEventDrivenHealthCheck)Activator.CreateInstance(eventDrivenType, h, a.Condition);
+
+ return Tuple.Create(a.EventType, eventDriven);
+ }))
.GroupBy(t => t.Item1, t => t.Item2)
.ToDictionary(g => g.Key, g => g.ToArray());
}
@@ -111,7 +116,7 @@ namespace NzbDrone.Core.HealthCheck
return;
}
- EventDrivenHealthCheck[] checks;
+ IEventDrivenHealthCheck[] checks;
if (!_eventDrivenHealthChecks.TryGetValue(message.GetType(), out checks))
{
return;
@@ -122,23 +127,10 @@ namespace NzbDrone.Core.HealthCheck
foreach (var eventDrivenHealthCheck in checks)
{
- if (eventDrivenHealthCheck.Condition == CheckOnCondition.Always)
- {
- filteredChecks.Add(eventDrivenHealthCheck.HealthCheck);
- continue;
- }
-
var healthCheckType = eventDrivenHealthCheck.HealthCheck.GetType();
+ var previouslyFailed = healthCheckResults.Any(r => r.Source == healthCheckType);
- if (eventDrivenHealthCheck.Condition == CheckOnCondition.FailedOnly &&
- healthCheckResults.Any(r => r.Source == healthCheckType))
- {
- filteredChecks.Add(eventDrivenHealthCheck.HealthCheck);
- continue;
- }
-
- if (eventDrivenHealthCheck.Condition == CheckOnCondition.SuccessfulOnly &&
- healthCheckResults.None(r => r.Source == healthCheckType))
+ if (eventDrivenHealthCheck.ShouldExecute(message, previouslyFailed))
{
filteredChecks.Add(eventDrivenHealthCheck.HealthCheck);
}
diff --git a/src/NzbDrone.Core/HealthCheck/ICheckOnCondition.cs b/src/NzbDrone.Core/HealthCheck/ICheckOnCondition.cs
new file mode 100644
index 000000000..921f1d3e8
--- /dev/null
+++ b/src/NzbDrone.Core/HealthCheck/ICheckOnCondition.cs
@@ -0,0 +1,7 @@
+namespace NzbDrone.Core.HealthCheck
+{
+ public interface ICheckOnCondition
+ {
+ bool ShouldCheckOnEvent(TEvent message);
+ }
+}
diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs
index 9707c9237..d1e98ddcf 100644
--- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs
+++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs
@@ -100,7 +100,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
if (response.StatusCode == HttpStatusCode.NotFound)
{
- throw new MovieNotFoundException("Movie not found.");
+ throw new MovieNotFoundException(tmdbId);
}
if (response.StatusCode != HttpStatusCode.OK)
diff --git a/src/NzbDrone.Core/Movies/MovieStatusType.cs b/src/NzbDrone.Core/Movies/MovieStatusType.cs
index 2e856e0b5..a4c7ec9bf 100644
--- a/src/NzbDrone.Core/Movies/MovieStatusType.cs
+++ b/src/NzbDrone.Core/Movies/MovieStatusType.cs
@@ -1,7 +1,8 @@
-namespace NzbDrone.Core.Movies
+namespace NzbDrone.Core.Movies
{
public enum MovieStatusType
{
+ Deleted = -1,
TBA = 0, //Nothing yet announced, only rumors, but still IMDb page (this might not be used)
Announced = 1, //Movie is announced but Cinema date is in the future or unknown
InCinemas = 2, //Been in Cinemas for less than 3 months (since TMDB lacks complete information)
diff --git a/src/NzbDrone.Core/Movies/RefreshMovieService.cs b/src/NzbDrone.Core/Movies/RefreshMovieService.cs
index 4a8327ac3..a6f13719f 100644
--- a/src/NzbDrone.Core/Movies/RefreshMovieService.cs
+++ b/src/NzbDrone.Core/Movies/RefreshMovieService.cs
@@ -63,13 +63,31 @@ namespace NzbDrone.Core.Movies
{
_logger.ProgressInfo("Updating Info for {0}", movie.Title);
- var tuple = _movieInfo.GetMovieInfo(movie.TmdbId, movie.Profile, movie.HasPreDBEntry);
+ Movie movieInfo;
+ List credits;
- var movieInfo = tuple.Item1;
+ try
+ {
+ var tuple = _movieInfo.GetMovieInfo(movie.TmdbId, movie.Profile, movie.HasPreDBEntry);
+ movieInfo = tuple.Item1;
+ credits = tuple.Item2;
+ }
+ catch (MovieNotFoundException)
+ {
+ if (movie.Status != MovieStatusType.Deleted)
+ {
+ movie.Status = MovieStatusType.Deleted;
+ _movieService.UpdateMovie(movie);
+ _logger.Debug("Movie marked as deleted on tmdb for {0}", movie.Title);
+ _eventAggregator.PublishEvent(new MovieUpdatedEvent(movie));
+ }
+
+ throw;
+ }
if (movie.TmdbId != movieInfo.TmdbId)
{
- _logger.Warn("Movie '{0}' (tvdbid {1}) was replaced with '{2}' (tvdbid {3}), because the original was a duplicate.", movie.Title, movie.TmdbId, movieInfo.Title, movieInfo.TmdbId);
+ _logger.Warn("Movie '{0}' (TmdbId {1}) was replaced with '{2}' (TmdbId {3}), because the original was a duplicate.", movie.Title, movie.TmdbId, movieInfo.Title, movieInfo.TmdbId);
movie.TmdbId = movieInfo.TmdbId;
}
@@ -139,7 +157,7 @@ namespace NzbDrone.Core.Movies
}
_movieService.UpdateMovie(new List { movie }, true);
- _creditService.UpdateCredits(tuple.Item2, movie);
+ _creditService.UpdateCredits(credits, movie);
try
{