using System; using System.IO; using System.Net; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Exceptions; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Movies; using NzbDrone.Core.Movies.Events; namespace NzbDrone.Core.MediaFiles { public interface IDeleteMediaFiles { void DeleteMovieFile(Movie movie, MovieFile movieFile); } public class MediaFileDeletionService : IDeleteMediaFiles, IHandleAsync, IHandle { private readonly IDiskProvider _diskProvider; private readonly IRecycleBinProvider _recycleBinProvider; private readonly IMediaFileService _mediaFileService; private readonly IMovieService _movieService; private readonly IConfigService _configService; private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; public MediaFileDeletionService(IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, IMediaFileService mediaFileService, IMovieService movieService, IConfigService configService, IEventAggregator eventAggregator, Logger logger) { _diskProvider = diskProvider; _recycleBinProvider = recycleBinProvider; _mediaFileService = mediaFileService; _movieService = movieService; _configService = configService; _eventAggregator = eventAggregator; _logger = logger; } public void DeleteMovieFile(Movie movie, MovieFile movieFile) { var fullPath = Path.Combine(movie.Path, movieFile.RelativePath); var rootFolder = _diskProvider.GetParentFolder(movie.Path); if (!_diskProvider.FolderExists(rootFolder)) { _logger.Warn("Movie's root folder ({0}) doesn't exist.", rootFolder); throw new NzbDroneClientException(HttpStatusCode.Conflict, "Movie's root folder ({0}) doesn't exist.", rootFolder); } if (_diskProvider.GetDirectories(rootFolder).Empty()) { _logger.Warn("Movie's root folder ({0}) is empty. Rescan will not update movies as a failsafe.", rootFolder); throw new NzbDroneClientException(HttpStatusCode.Conflict, "Movie's root folder ({0}) is empty. Rescan will not update movies as a failsafe.", rootFolder); } if (_diskProvider.FolderExists(movie.Path) && _diskProvider.FileExists(fullPath)) { _logger.Info("Deleting movie file: {0}", fullPath); var subfolder = _diskProvider.GetParentFolder(movie.Path).GetRelativePath(_diskProvider.GetParentFolder(fullPath)); try { _recycleBinProvider.DeleteFile(fullPath, subfolder); } catch (Exception e) { _logger.Error(e, "Unable to delete movie file"); throw new NzbDroneClientException(HttpStatusCode.InternalServerError, "Unable to delete movie file"); } } // Delete the movie file from the database to clean it up even if the file was already deleted _mediaFileService.Delete(movieFile, DeleteMediaFileReason.Manual); _eventAggregator.PublishEvent(new DeleteCompletedEvent()); } public void HandleAsync(MoviesDeletedEvent message) { if (message.DeleteFiles) { var allMovies = _movieService.AllMoviePaths(); foreach (var movie in message.Movies) { foreach (var s in allMovies) { if (s.Key == movie.Id) { continue; } if (movie.Path.IsParentPath(s.Value)) { _logger.Error("Movie path: '{0}' is a parent of another movie, not deleting files.", movie.Path); return; } if (movie.Path.PathEquals(s.Value)) { _logger.Error("Movie path: '{0}' is the same as another movie, not deleting files.", movie.Path); return; } } if (_diskProvider.FolderExists(movie.Path)) { _recycleBinProvider.DeleteFolder(movie.Path); } } _eventAggregator.PublishEvent(new DeleteCompletedEvent()); } } [EventHandleOrder(EventHandleOrder.Last)] public void Handle(MovieFileDeletedEvent message) { if (_configService.DeleteEmptyFolders) { var movie = message.MovieFile.Movie; var moviePath = movie.Path; var folder = message.MovieFile.Path.GetParentPath(); while (moviePath.IsParentPath(folder)) { if (_diskProvider.FolderExists(folder)) { _diskProvider.RemoveEmptySubfolders(folder); } folder = folder.GetParentPath(); } _diskProvider.RemoveEmptySubfolders(moviePath); if (_diskProvider.FolderEmpty(moviePath)) { _diskProvider.DeleteFolder(moviePath, true); } } } } }