mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-05 02:22:31 +01:00
New: Show episode file file deletions in history and episode activity
This commit is contained in:
parent
5d60b21dba
commit
f63476260b
@ -80,7 +80,7 @@ private void DeleteEpisodeFile(int id)
|
||||
|
||||
_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)
|
||||
|
@ -5,6 +5,7 @@
|
||||
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 @@ public void Handle(EpisodeImportedEvent message)
|
||||
|
||||
public void Handle(EpisodeFileDeletedEvent message)
|
||||
{
|
||||
if (message.ForUpgrade) return;
|
||||
if (message.Reason == DeleteMediaFileReason.Upgrade) return;
|
||||
|
||||
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.SeriesId);
|
||||
}
|
||||
|
@ -93,7 +93,9 @@ public void one_to_one()
|
||||
options => options
|
||||
.IncludingAllRuntimeProperties()
|
||||
.Excluding(c => c.DateAdded)
|
||||
.Excluding(c => c.Path));
|
||||
.Excluding(c => c.Path)
|
||||
.Excluding(c => c.Series)
|
||||
.Excluding(c => c.Episodes));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -79,7 +79,7 @@ public void should_delete_non_existent_files()
|
||||
|
||||
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 @@ public void should_delete_files_that_dont_belong_to_any_episodes()
|
||||
|
||||
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]
|
||||
|
@ -126,7 +126,7 @@ public void should_delete_episode_file_from_database()
|
||||
|
||||
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 @@ public void should_delete_existing_file_fromdb_if_file_doesnt_exist()
|
||||
|
||||
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]
|
||||
|
@ -58,7 +58,7 @@ public void should_set_EpisodeFileId_to_zero()
|
||||
{
|
||||
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 @@ public void should_update_each_episode_for_file()
|
||||
{
|
||||
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 @@ public void should_set_monitored_to_false_if_autoUnmonitor_is_true_and_is_not_fo
|
||||
.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 @@ public void should_leave_monitored_to_true_if_autoUnmonitor_is_false()
|
||||
.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 @@ public void should_leave_monitored_to_true_if_autoUnmonitor_is_true_and_is_for_a
|
||||
.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());
|
||||
|
@ -64,7 +64,11 @@ public static void Map()
|
||||
|
||||
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)
|
||||
|
@ -30,6 +30,7 @@ public enum HistoryEventType
|
||||
Grabbed = 1,
|
||||
SeriesFolderImported = 2,
|
||||
DownloadFolderImported = 3,
|
||||
DownloadFailed = 4
|
||||
DownloadFailed = 4,
|
||||
EpisodeFileDeleted = 5
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
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 @@ public interface IHistoryService
|
||||
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 @@ public void Handle(DownloadFailedEvent message)
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
src/NzbDrone.Core/MediaFiles/DeleteMediaFileReason.cs
Normal file
10
src/NzbDrone.Core/MediaFiles/DeleteMediaFileReason.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
public enum DeleteMediaFileReason
|
||||
{
|
||||
MissingFromDisk,
|
||||
Manual,
|
||||
Upgrade,
|
||||
NoLinkedEpisodes
|
||||
}
|
||||
}
|
@ -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 @@ public class EpisodeFile : ModelBase
|
||||
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()
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
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 @@ public interface IMediaFileService
|
||||
{
|
||||
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 @@ public void Update(EpisodeFile episodeFile)
|
||||
_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)
|
||||
|
@ -47,14 +47,14 @@ public void Execute(CleanMediaFileDb message)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ public EpisodeFileMoveResult UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEp
|
||||
}
|
||||
|
||||
moveFileResult.OldFiles.Add(file);
|
||||
_mediaFileService.Delete(file, true);
|
||||
_mediaFileService.Delete(file, DeleteMediaFileReason.Upgrade);
|
||||
}
|
||||
|
||||
if (copyOnly)
|
||||
|
@ -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>
|
||||
|
@ -4,6 +4,7 @@
|
||||
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 @@ public void Handle(EpisodeFileDeletedEvent message)
|
||||
_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;
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -195,3 +195,7 @@
|
||||
.icon-nd-manual-search:before {
|
||||
.icon(@user);
|
||||
}
|
||||
|
||||
.icon-nd-deleted:before {
|
||||
.icon(@trash);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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}}
|
Loading…
Reference in New Issue
Block a user