New: Show episode file file deletions in history and episode activity

This commit is contained in:
Mark McDowall 2014-08-20 23:29:34 -07:00
parent 5d60b21dba
commit f63476260b
21 changed files with 122 additions and 29 deletions

View File

@ -80,7 +80,7 @@ namespace NzbDrone.Api.EpisodeFiles
_logger.Info("Deleting episode file: {0}", fullPath);
_recycleBinProvider.DeleteFile(fullPath);
_mediaFileService.Delete(episodeFile);
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
}
private EpisodeFileResource MapToResource(Core.Tv.Series series, EpisodeFile episodeFile)

View File

@ -5,6 +5,7 @@ using FluentValidation;
using NzbDrone.Common;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
@ -188,7 +189,7 @@ namespace NzbDrone.Api.Series
public void Handle(EpisodeFileDeletedEvent message)
{
if (message.ForUpgrade) return;
if (message.Reason == DeleteMediaFileReason.Upgrade) return;
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.SeriesId);
}

View File

@ -93,7 +93,9 @@ namespace NzbDrone.Core.Test.Datastore
options => options
.IncludingAllRuntimeProperties()
.Excluding(c => c.DateAdded)
.Excluding(c => c.Path));
.Excluding(c => c.Path)
.Excluding(c => c.Series)
.Excluding(c => c.Episodes));
}
[Test]

View File

@ -79,7 +79,7 @@ namespace NzbDrone.Core.Test.MediaFiles
Subject.Execute(new CleanMediaFileDb(0));
Mocker.GetMock<IMediaFileService>().Verify(c => c.Delete(It.Is<EpisodeFile>(e => e.RelativePath == DELETED_PATH), false), Times.Exactly(2));
Mocker.GetMock<IMediaFileService>().Verify(c => c.Delete(It.Is<EpisodeFile>(e => e.RelativePath == DELETED_PATH), DeleteMediaFileReason.MissingFromDisk), Times.Exactly(2));
}
[Test]
@ -95,7 +95,7 @@ namespace NzbDrone.Core.Test.MediaFiles
Subject.Execute(new CleanMediaFileDb(0));
Mocker.GetMock<IMediaFileService>().Verify(c => c.Delete(It.IsAny<EpisodeFile>(), false), Times.Exactly(10));
Mocker.GetMock<IMediaFileService>().Verify(c => c.Delete(It.IsAny<EpisodeFile>(), DeleteMediaFileReason.NoLinkedEpisodes), Times.Exactly(10));
}
[Test]

View File

@ -126,7 +126,7 @@ namespace NzbDrone.Core.Test.MediaFiles
Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(It.IsAny<EpisodeFile>(), true), Times.Once());
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(It.IsAny<EpisodeFile>(), DeleteMediaFileReason.Upgrade), Times.Once());
}
[Test]
@ -140,7 +140,7 @@ namespace NzbDrone.Core.Test.MediaFiles
Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(_localEpisode.Episodes.Single().EpisodeFile.Value, true), Times.Once());
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(_localEpisode.Episodes.Single().EpisodeFile.Value, DeleteMediaFileReason.Upgrade), Times.Once());
}
[Test]

View File

@ -58,7 +58,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeProviderTests
{
GivenSingleEpisodeFile();
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, false));
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.MissingFromDisk));
Mocker.GetMock<IEpisodeRepository>()
.Verify(v => v.Update(It.Is<Episode>(e => e.EpisodeFileId == 0)), Times.Once());
@ -69,7 +69,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeProviderTests
{
GivenMultiEpisodeFile();
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, false));
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.MissingFromDisk));
Mocker.GetMock<IEpisodeRepository>()
.Verify(v => v.Update(It.Is<Episode>(e => e.EpisodeFileId == 0)), Times.Exactly(2));
@ -84,7 +84,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeProviderTests
.SetupGet(s => s.AutoUnmonitorPreviouslyDownloadedEpisodes)
.Returns(true);
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, false));
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.MissingFromDisk));
Mocker.GetMock<IEpisodeRepository>()
.Verify(v => v.Update(It.Is<Episode>(e => e.Monitored == false)), Times.Once());
@ -99,7 +99,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeProviderTests
.SetupGet(s => s.AutoUnmonitorPreviouslyDownloadedEpisodes)
.Returns(false);
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, false));
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.Upgrade));
Mocker.GetMock<IEpisodeRepository>()
.Verify(v => v.Update(It.Is<Episode>(e => e.Monitored == true)), Times.Once());
@ -114,7 +114,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeProviderTests
.SetupGet(s => s.AutoUnmonitorPreviouslyDownloadedEpisodes)
.Returns(true);
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, true));
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.Upgrade));
Mocker.GetMock<IEpisodeRepository>()
.Verify(v => v.Update(It.Is<Episode>(e => e.Monitored == true)), Times.Once());

View File

@ -64,7 +64,11 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<EpisodeFile>().RegisterModel("EpisodeFiles")
.Ignore(f => f.Path)
.Relationships.AutoMapICollectionOrComplexProperties();
.Relationships.AutoMapICollectionOrComplexProperties()
.For("Episodes")
.LazyLoad(condition: parent => parent.Id > 0,
query: (db, parent) => db.Query<Episode>().Where(c => c.EpisodeFileId == parent.Id).ToList())
.HasOne(file => file.Series, file => file.SeriesId);
Mapper.Entity<Episode>().RegisterModel("Episodes")
.Ignore(e => e.SeriesTitle)

View File

@ -30,6 +30,7 @@ namespace NzbDrone.Core.History
Grabbed = 1,
SeriesFolderImported = 2,
DownloadFolderImported = 3,
DownloadFailed = 4
DownloadFailed = 4,
EpisodeFileDeleted = 5
}
}

View File

@ -6,6 +6,7 @@ using NLog;
using NzbDrone.Common;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Profiles;
@ -31,7 +32,11 @@ namespace NzbDrone.Core.History
void UpdateHistoryData(Int32 historyId, Dictionary<String, String> data);
}
public class HistoryService : IHistoryService, IHandle<EpisodeGrabbedEvent>, IHandle<EpisodeImportedEvent>, IHandle<DownloadFailedEvent>
public class HistoryService : IHistoryService,
IHandle<EpisodeGrabbedEvent>,
IHandle<EpisodeImportedEvent>,
IHandle<DownloadFailedEvent>,
IHandle<EpisodeFileDeletedEvent>
{
private readonly IHistoryRepository _historyRepository;
private readonly Logger _logger;
@ -199,5 +204,31 @@ namespace NzbDrone.Core.History
_historyRepository.Insert(history);
}
}
public void Handle(EpisodeFileDeletedEvent message)
{
if (message.Reason == DeleteMediaFileReason.NoLinkedEpisodes)
{
_logger.Debug("Removing episode file from DB as part of cleanup routine.");
return;
}
foreach (var episode in message.EpisodeFile.Episodes.Value)
{
var history = new History
{
EventType = HistoryEventType.EpisodeFileDeleted,
Date = DateTime.UtcNow,
Quality = message.EpisodeFile.Quality,
SourceTitle = message.EpisodeFile.Path,
SeriesId = message.EpisodeFile.SeriesId,
EpisodeId = episode.Id,
};
history.Data.Add("Reason", message.Reason.ToString());
_historyRepository.Insert(history);
}
}
}
}

View File

@ -0,0 +1,10 @@
namespace NzbDrone.Core.MediaFiles
{
public enum DeleteMediaFileReason
{
MissingFromDisk,
Manual,
Upgrade,
NoLinkedEpisodes
}
}

View File

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using Marr.Data;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
@ -18,7 +20,8 @@ namespace NzbDrone.Core.MediaFiles
public String ReleaseGroup { get; set; }
public QualityModel Quality { get; set; }
public MediaInfoModel MediaInfo { get; set; }
public LazyList<Episode> Episodes { get; set; }
public LazyLoaded<List<Episode>> Episodes { get; set; }
public LazyLoaded<Series> Series { get; set; }
public override String ToString()
{

View File

@ -1,17 +1,16 @@
using System;
using NzbDrone.Common.Messaging;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.MediaFiles.Events
{
public class EpisodeFileDeletedEvent : IEvent
{
public EpisodeFile EpisodeFile { get; private set; }
public Boolean ForUpgrade { get; private set; }
public DeleteMediaFileReason Reason { get; private set; }
public EpisodeFileDeletedEvent(EpisodeFile episodeFile, Boolean forUpgrade)
public EpisodeFileDeletedEvent(EpisodeFile episodeFile, DeleteMediaFileReason reason)
{
EpisodeFile = episodeFile;
ForUpgrade = forUpgrade;
Reason = reason;
}
}
}

View File

@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
@ -14,7 +15,7 @@ namespace NzbDrone.Core.MediaFiles
{
EpisodeFile Add(EpisodeFile episodeFile);
void Update(EpisodeFile episodeFile);
void Delete(EpisodeFile episodeFile, bool forUpgrade = false);
void Delete(EpisodeFile episodeFile, DeleteMediaFileReason reason);
List<EpisodeFile> GetFilesBySeries(int seriesId);
List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber);
List<EpisodeFile> GetFilesWithoutMediaInfo();
@ -49,11 +50,14 @@ namespace NzbDrone.Core.MediaFiles
_mediaFileRepository.Update(episodeFile);
}
public void Delete(EpisodeFile episodeFile, bool forUpgrade = false)
public void Delete(EpisodeFile episodeFile, DeleteMediaFileReason reason)
{
_mediaFileRepository.Delete(episodeFile);
//Little hack so we have the episodes and series attached for the event consumers
episodeFile.Episodes.LazyLoad();
episodeFile.Path = Path.Combine(episodeFile.Series.Value.Path, episodeFile.RelativePath);
_eventAggregator.PublishEvent(new EpisodeFileDeletedEvent(episodeFile, forUpgrade));
_mediaFileRepository.Delete(episodeFile);
_eventAggregator.PublishEvent(new EpisodeFileDeletedEvent(episodeFile, reason));
}
public List<EpisodeFile> GetFilesBySeries(int seriesId)

View File

@ -47,14 +47,14 @@ namespace NzbDrone.Core.MediaFiles
if (!_diskProvider.FileExists(episodeFilePath))
{
_logger.Debug("File [{0}] no longer exists on disk, removing from db", episodeFilePath);
_mediaFileService.Delete(episodeFile);
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.MissingFromDisk);
continue;
}
if (!episodes.Any(e => e.EpisodeFileId == episodeFile.Id))
{
_logger.Debug("File [{0}] is not assigned to any episodes, removing from db", episodeFilePath);
_mediaFileService.Delete(episodeFile);
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.NoLinkedEpisodes);
continue;
}

View File

@ -53,7 +53,7 @@ namespace NzbDrone.Core.MediaFiles
}
moveFileResult.OldFiles.Add(file);
_mediaFileService.Delete(file, true);
_mediaFileService.Delete(file, DeleteMediaFileReason.Upgrade);
}
if (copyOnly)

View File

@ -460,6 +460,7 @@
<Compile Include="MediaFiles\Commands\RenameFilesCommand.cs" />
<Compile Include="MediaFiles\Commands\RenameSeriesCommand.cs" />
<Compile Include="MediaFiles\Commands\RescanSeriesCommand.cs" />
<Compile Include="MediaFiles\DeleteMediaFileReason.cs" />
<Compile Include="MediaFiles\DiskScanService.cs">
<SubType>Code</SubType>
</Compile>

View File

@ -4,6 +4,7 @@ using System.Linq;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv.Events;
@ -185,7 +186,7 @@ namespace NzbDrone.Core.Tv
_logger.Debug("Detaching episode {0} from file.", episode.Id);
episode.EpisodeFileId = 0;
if (!message.ForUpgrade && _configService.AutoUnmonitorPreviouslyDownloadedEpisodes)
if (message.Reason != DeleteMediaFileReason.Upgrade && _configService.AutoUnmonitorPreviouslyDownloadedEpisodes)
{
episode.Monitored = false;
}

View File

@ -33,6 +33,10 @@ define(
icon = 'icon-nd-download-failed';
toolTip = 'Episode download failed';
break;
case 'episodeFileDeleted':
icon = 'icon-nd-deleted';
toolTip = 'Episode file deleted';
break;
default :
icon = 'icon-question';
toolTip = 'unknown event';

View File

@ -195,3 +195,7 @@
.icon-nd-manual-search:before {
.icon(@user);
}
.icon-nd-deleted:before {
.icon(@trash);
}

View File

@ -7,6 +7,7 @@
{{#if_eq eventType compare="grabbed"}}Grabbed{{/if_eq}}
{{#if_eq eventType compare="downloadFailed"}}Download Failed{{/if_eq}}
{{#if_eq eventType compare="downloadFolderImported"}}Episode Imported{{/if_eq}}
{{#if_eq eventType compare="episodeFileDeleted"}}Episode File Deleted{{/if_eq}}
</h3>
</div>

View File

@ -36,6 +36,7 @@
{{/with}}
</dl>
{{/if_eq}}
{{#if_eq eventType compare="downloadFailed"}}
<dl class="dl-horizontal">
@ -48,6 +49,7 @@
{{/with}}
</dl>
{{/if_eq}}
{{#if_eq eventType compare="downloadFolderImported"}}
<dl class="dl-horizontal">
@ -68,4 +70,29 @@
{{/if}}
{{/with}}
</dl>
{{/if_eq}}
{{#if_eq eventType compare="episodeFileDeleted"}}
<dl class="dl-horizontal">
<dt>Path:</dt>
<dd>{{sourceTitle}}</dd>
{{#with data}}
<dt>Reason:</dt>
<dd>
{{#if_eq reason compare="Manual"}}
File was deleted by via UI
{{/if_eq}}
{{#if_eq reason compare="MissingFromDisk"}}
NzbDrone was unable to find the file on disk so it was removed
{{/if_eq}}
{{#if_eq reason compare="Upgrade"}}
File was deleted to imported an upgrade
{{/if_eq}}
</dd>
{{/with}}
</dl>
{{/if_eq}}