Example #1
0
        private object GetMediaFiles()
        {
            var pathQuery = Request.Query.path;
            var path      = (string)pathQuery.Value;

            if (!_diskProvider.FolderExists(path))
            {
                return(new string[0]);
            }

            return(_diskScanService.GetBookFiles(path).Select(f => new
            {
                Path = f.FullName,
                Name = f.Name
            }));
        }
Example #2
0
        private Response GetMediaFiles()
        {
            var pathQuery = Request.Query.path;
            var path      = (string)pathQuery.Value;

            if (!_diskProvider.FolderExists(path))
            {
                return(new string[0].AsResponse());
            }

            return(_diskScanService.GetVideoFiles(path).Select(f => new {
                Path = f,
                RelativePath = path.GetRelativePath(f),
                Name = Path.GetFileName(f)
            }).AsResponse());
        }
Example #3
0
        public TrackFileMoveResult UpgradeTrackFile(TrackFile trackFile, LocalTrack localTrack, bool copyOnly = false)
        {
            var moveFileResult = new TrackFileMoveResult();
            var existingFiles  = localTrack.Tracks
                                 .Where(e => e.TrackFileId > 0)
                                 .Select(e => e.TrackFile.Value)
                                 .Where(e => e != null)
                                 .GroupBy(e => e.Id)
                                 .ToList();

            var rootFolder = _diskProvider.GetParentFolder(localTrack.Artist.Path);

            // If there are existing track files and the root folder is missing, throw, so the old file isn't left behind during the import process.
            if (existingFiles.Any() && !_diskProvider.FolderExists(rootFolder))
            {
                throw new RootFolderNotFoundException($"Root folder '{rootFolder}' was not found.");
            }

            foreach (var existingFile in existingFiles)
            {
                var file          = existingFile.First();
                var trackFilePath = file.Path;
                var subfolder     = rootFolder.GetRelativePath(_diskProvider.GetParentFolder(trackFilePath));

                if (_diskProvider.FileExists(trackFilePath))
                {
                    _logger.Debug("Removing existing track file: {0}", file);
                    _recycleBinProvider.DeleteFile(trackFilePath, subfolder);
                }

                moveFileResult.OldFiles.Add(file);
                _mediaFileService.Delete(file, DeleteMediaFileReason.Upgrade);
            }

            if (copyOnly)
            {
                moveFileResult.TrackFile = _trackFileMover.CopyTrackFile(trackFile, localTrack);
            }
            else
            {
                moveFileResult.TrackFile = _trackFileMover.MoveTrackFile(trackFile, localTrack);
            }

            _audioTagService.WriteTags(trackFile, true);

            return(moveFileResult);
        }
Example #4
0
        private void Scan(Series series)
        {
            _logger.ProgressInfo("Scanning disk for {0}", series.Title);
            _commandExecutor.PublishCommand(new CleanMediaFileDb(series.Id));

            if (!_diskProvider.FolderExists(series.Path))
            {
                _logger.Debug("Series folder doesn't exist: {0}", series.Path);
                return;
            }

            var mediaFileList = GetVideoFiles(series.Path);

            var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, series, false);

            _importApprovedEpisodes.Import(decisions);
        }
Example #5
0
        private object GetMediaFiles()
        {
            var pathQuery = Request.Query.path;
            var path      = (string)pathQuery.Value;

            if (!_diskProvider.FolderExists(path))
            {
                return(Array.Empty <string>());
            }

            return(_diskScanService.GetVideoFiles(path).Select(f => new
            {
                Path = f,
                RelativePath = path.GetRelativePath(f),
                Name = Path.GetFileName(f)
            }));
        }
        public EpisodeFileMoveResult UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode, bool copyOnly = false)
        {
            var moveFileResult = new EpisodeFileMoveResult();
            var existingFiles  = localEpisode.Episodes
                                 .Where(e => e.EpisodeFileId > 0)
                                 .Select(e => e.EpisodeFile.Value)
                                 .Where(e => e != null)
                                 .GroupBy(e => e.Id)
                                 .ToList();

            var rootFolder = _diskProvider.GetParentFolder(localEpisode.Series.Path);

            // If there are existing episode files and the root folder is missing, throw, so the old file isn't left behind during the import process.
            if (existingFiles.Any() && !_diskProvider.FolderExists(rootFolder))
            {
                throw new RootFolderNotFoundException($"Root folder '{rootFolder}' was not found.");
            }

            foreach (var existingFile in existingFiles)
            {
                var file            = existingFile.First();
                var episodeFilePath = Path.Combine(localEpisode.Series.Path, file.RelativePath);
                var subfolder       = rootFolder.GetRelativePath(_diskProvider.GetParentFolder(episodeFilePath));

                if (_diskProvider.FileExists(episodeFilePath))
                {
                    _logger.Debug("Removing existing episode file: {0}", file);
                    _recycleBinProvider.DeleteFile(episodeFilePath, subfolder);
                }

                moveFileResult.OldFiles.Add(file);
                _mediaFileService.Delete(file, DeleteMediaFileReason.Upgrade);
            }

            if (copyOnly)
            {
                moveFileResult.EpisodeFile = _episodeFileMover.CopyEpisodeFile(episodeFile, localEpisode);
            }
            else
            {
                moveFileResult.EpisodeFile = _episodeFileMover.MoveEpisodeFile(episodeFile, localEpisode);
            }

            return(moveFileResult);
        }
        public List <ImportResult> ProcessPath(string path, Series series = null, DownloadClientItem downloadClientItem = null)
        {
            if (_diskProvider.FolderExists(path))
            {
                if (series == null)
                {
                    return(ProcessFolder(new DirectoryInfo(path), downloadClientItem));
                }

                return(ProcessFolder(new DirectoryInfo(path), series, downloadClientItem));
            }

            if (series == null)
            {
                return(ProcessFile(new FileInfo(path), downloadClientItem));
            }

            return(ProcessFile(new FileInfo(path), series, downloadClientItem));
        }
Example #8
0
        public override HealthCheck Check()
        {
            var rootFolders = _seriesService.GetAllSeriesPaths().Select(s => _rootFolderService.GetBestRootFolderPath(s)).Distinct();

            var missingRootFolders = rootFolders.Where(s => !_diskProvider.FolderExists(s))
                                     .ToList();

            if (missingRootFolders.Any())
            {
                if (missingRootFolders.Count == 1)
                {
                    return(new HealthCheck(GetType(), HealthCheckResult.Error, "Missing root folder: " + missingRootFolders.First(), "#missing-root-folder"));
                }

                var message = string.Format("Multiple root folders are missing: {0}", string.Join(" | ", missingRootFolders));
                return(new HealthCheck(GetType(), HealthCheckResult.Error, message, "#missing-root-folder"));
            }

            return(new HealthCheck(GetType()));
        }
Example #9
0
        public override IEnumerable <ExtraFile> CreateAfterArtistScan(Artist artist, List <TrackFile> trackFiles)
        {
            var metadataFiles = _metadataFileService.GetFilesByArtist(artist.Id);

            _cleanMetadataService.Clean(artist);

            if (!_diskProvider.FolderExists(artist.Path))
            {
                _logger.Info("Artist folder does not exist, skipping metadata creation");
                return(Enumerable.Empty <MetadataFile>());
            }

            var files = new List <MetadataFile>();

            foreach (var consumer in _metadataFactory.Enabled())
            {
                var consumerFiles = GetMetadataFilesForConsumer(consumer, metadataFiles);

                files.AddIfNotNull(ProcessArtistMetadata(consumer, artist, consumerFiles));
                files.AddRange(ProcessArtistImages(consumer, artist, consumerFiles));

                var albumGroups = trackFiles.GroupBy(s => Path.GetDirectoryName(s.Path)).ToList();

                foreach (var group in albumGroups)
                {
                    var album       = _albumService.GetAlbum(group.First().AlbumId);
                    var albumFolder = group.Key;
                    files.AddIfNotNull(ProcessAlbumMetadata(consumer, artist, album, albumFolder, consumerFiles));
                    files.AddRange(ProcessAlbumImages(consumer, artist, album, albumFolder, consumerFiles));

                    foreach (var trackFile in group)
                    {
                        files.AddIfNotNull(ProcessTrackMetadata(consumer, artist, trackFile, consumerFiles));
                    }
                }
            }

            _metadataFileService.Upsert(files);

            return(files);
        }
        public MovieFileMoveResult UpgradeMovieFile(MovieFile movieFile, LocalMovie localMovie, bool copyOnly = false)
        {
            _logger.Trace("Upgrading existing movie file.");
            var moveFileResult = new MovieFileMoveResult();

            var existingFile = localMovie.Movie.MovieFileId > 0 ? localMovie.Movie.MovieFile : null;

            var rootFolder = _diskProvider.GetParentFolder(localMovie.Movie.Path);

            // If there are existing movie files and the root folder is missing, throw, so the old file isn't left behind during the import process.
            if (existingFile != null && !_diskProvider.FolderExists(rootFolder))
            {
                throw new RootFolderNotFoundException($"Root folder '{rootFolder}' was not found.");
            }

            if (existingFile != null)
            {
                var movieFilePath = Path.Combine(localMovie.Movie.Path, existingFile.RelativePath);
                var subfolder     = rootFolder.GetRelativePath(_diskProvider.GetParentFolder(movieFilePath));

                if (_diskProvider.FileExists(movieFilePath))
                {
                    _logger.Debug("Removing existing movie file: {0}", existingFile);
                    _recycleBinProvider.DeleteFile(movieFilePath, subfolder);
                }

                moveFileResult.OldFiles.Add(existingFile);
                _mediaFileService.Delete(existingFile, DeleteMediaFileReason.Upgrade);
            }

            if (copyOnly)
            {
                moveFileResult.MovieFile = _movieFileMover.CopyMovieFile(movieFile, localMovie);
            }
            else
            {
                moveFileResult.MovieFile = _movieFileMover.MoveMovieFile(movieFile, localMovie);
            }

            return(moveFileResult);
        }
        protected ValidationFailure TestFolder(string folder, string propertyName, bool mustBeWritable = true)
        {
            if (!_diskProvider.FolderExists(folder))
            {
                return(new NzbDroneValidationFailure(propertyName, "Folder does not exist")
                {
                    DetailedDescription = string.Format("The folder you specified does not exist or is inaccessible. Please verify the folder permissions for the user account '{0}', which is used to execute Prowlarr.", Environment.UserName)
                });
            }

            if (mustBeWritable && !_diskProvider.FolderWritable(folder))
            {
                _logger.Error("Folder '{0}' is not writable.", folder);
                return(new NzbDroneValidationFailure(propertyName, "Unable to write to folder")
                {
                    DetailedDescription = string.Format("The folder you specified is not writable. Please verify the folder permissions for the user account '{0}', which is used to execute Prowlarr.", Environment.UserName)
                });
            }

            return(null);
        }
Example #12
0
        private void MoveSingleArtist(Artist artist, string sourcePath, string destinationPath, bool moveFiles, int?index = null, int?total = null)
        {
            if (!_diskProvider.FolderExists(sourcePath))
            {
                _logger.Debug("Folder '{0}' for '{1}' does not exist, not moving.", sourcePath, artist.Name);
                return;
            }

            if (index != null && total != null)
            {
                _logger.ProgressInfo("Moving {0} from '{1}' to '{2}' ({3}/{4})", artist.Name, sourcePath, destinationPath, index + 1, total);
            }
            else
            {
                _logger.ProgressInfo("Moving {0} from '{1}' to '{2}'", artist.Name, sourcePath, destinationPath);
            }

            try
            {
                if (moveFiles)
                {
                    _rootFolderWatchingService.ReportFileSystemChangeBeginning(sourcePath, destinationPath);

                    // Ensure the parent of the artist folder exists, this will often just be the root folder, but
                    // in cases where people are using subfolders for first letter (etc) it may not yet exist.
                    _diskProvider.CreateFolder(new DirectoryInfo(destinationPath).Parent.FullName);
                    _diskTransferService.TransferFolder(sourcePath, destinationPath, TransferMode.Move);

                    _logger.ProgressInfo("{0} moved successfully to {1}", artist.Name, artist.Path);
                }

                _eventAggregator.PublishEvent(new ArtistMovedEvent(artist, sourcePath, destinationPath));
            }
            catch (IOException ex)
            {
                _logger.Error(ex, "Unable to move artist from '{0}' to '{1}'. Try moving files manually", sourcePath, destinationPath);

                RevertPath(artist.Id, sourcePath);
            }
        }
Example #13
0
        public override HealthCheck Check()
        {
            var rootFolders = _movieService.AllMoviePaths()
                              .Select(s => _rootFolderService.GetBestRootFolderPath(s.Value))
                              .Distinct();

            var missingRootFolders = rootFolders.Where(s => !_diskProvider.FolderExists(s))
                                     .ToList();

            if (missingRootFolders.Any())
            {
                if (missingRootFolders.Count == 1)
                {
                    return(new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RootFolderCheckSingleMessage"), missingRootFolders.First()), "#missing-root-folder"));
                }

                var message = string.Format(_localizationService.GetLocalizedString("RootFolderCheckMultipleMessage"), string.Join(" | ", missingRootFolders));
                return(new HealthCheck(GetType(), HealthCheckResult.Error, message, "#missing-root-folder"));
            }

            return(new HealthCheck(GetType()));
        }
Example #14
0
        public override HealthCheck Check()
        {
            var importLists        = _importListFactory.All();
            var missingRootFolders = new Dictionary <string, List <ImportListDefinition> >();

            foreach (var importList in importLists)
            {
                var rootFolderPath = importList.RootFolderPath;

                if (missingRootFolders.ContainsKey(rootFolderPath))
                {
                    missingRootFolders[rootFolderPath].Add(importList);

                    continue;
                }

                if (rootFolderPath.IsNullOrWhiteSpace() || !_diskProvider.FolderExists(rootFolderPath))
                {
                    missingRootFolders.Add(rootFolderPath, new List <ImportListDefinition> {
                        importList
                    });
                }
            }

            if (missingRootFolders.Any())
            {
                if (missingRootFolders.Count == 1)
                {
                    var missingRootFolder = missingRootFolders.First();
                    return(new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ImportListMissingRoot"), FormatRootFolder(missingRootFolder.Key, missingRootFolder.Value)), "#import-list-missing-root-folder"));
                }

                var message = string.Format(_localizationService.GetLocalizedString("ImportListMultipleMissingRoots"), string.Join(" | ", missingRootFolders.Select(m => FormatRootFolder(m.Key, m.Value))));
                return(new HealthCheck(GetType(), HealthCheckResult.Error, message, "#import-list-missing-root-folder"));
            }

            return(new HealthCheck(GetType()));
        }
        public override HealthCheck Check()
        {
            var importLists        = _importListFactory.All();
            var missingRootFolders = new Dictionary <string, List <ImportListDefinition> >();

            foreach (var importList in importLists)
            {
                var rootFolderPath = importList.RootFolderPath;

                if (missingRootFolders.ContainsKey(rootFolderPath))
                {
                    missingRootFolders[rootFolderPath].Add(importList);

                    continue;
                }

                if (!_diskProvider.FolderExists(rootFolderPath))
                {
                    missingRootFolders.Add(rootFolderPath, new List <ImportListDefinition> {
                        importList
                    });
                }
            }

            if (missingRootFolders.Any())
            {
                if (missingRootFolders.Count == 1)
                {
                    var missingRootFolder = missingRootFolders.First();
                    return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Missing root folder for import list(s): {FormatRootFolder(missingRootFolder.Key, missingRootFolder.Value)}", "#import-list-missing-root-folder"));
                }

                var message = string.Format("Multiple root folders are missing for import lists: {0}", string.Join(" | ", missingRootFolders.Select(m => FormatRootFolder(m.Key, m.Value))));
                return(new HealthCheck(GetType(), HealthCheckResult.Error, message, "#import-list-missing-root-folder"));
            }

            return(new HealthCheck(GetType()));
        }
Example #16
0
        public override HealthCheck Check()
        {
            var droneFactoryFolder = _configService.DownloadedEpisodesFolder;

            if (droneFactoryFolder.IsNullOrWhiteSpace())
            {
                return(new HealthCheck(GetType()));
            }

            if (!_diskProvider.FolderExists(droneFactoryFolder))
            {
                return(new HealthCheck(GetType(), HealthCheckResult.Error, "Drone factory folder does not exist"));
            }

            if (!_diskProvider.FolderWritable(droneFactoryFolder))
            {
                return(new HealthCheck(GetType(), HealthCheckResult.Error, "Unable to write to drone factory folder"));
            }

            //Todo: Unable to import one or more files/folders from

            return(new HealthCheck(GetType()));
        }
        private void InstallUpdate(UpdatePackage updatePackage)
        {
            try
            {
                var updateSandboxFolder = _appFolderInfo.GetUpdateSandboxFolder();

                var packageDestination = Path.Combine(updateSandboxFolder, updatePackage.FileName);

                if (_diskProvider.FolderExists(updateSandboxFolder))
                {
                    _logger.Info("Deleting old update files");
                    _diskProvider.DeleteFolder(updateSandboxFolder, true);
                }

                _logger.ProgressInfo("Downloading update {0} [{1}]", updatePackage.Version, updatePackage.Branch);
                _logger.Debug("Downloading update package from [{0}] to [{1}]", updatePackage.Url, packageDestination);
                _httpProvider.DownloadFile(updatePackage.Url, packageDestination);

                _logger.ProgressInfo("Extracting Update package");
                _archiveService.Extract(packageDestination, updateSandboxFolder);
                _logger.Info("Update package extracted successfully");

                _logger.Info("Preparing client");
                _diskProvider.MoveFolder(_appFolderInfo.GetUpdateClientFolder(),
                                         updateSandboxFolder);

                _logger.Info("Starting update client {0}", _appFolderInfo.GetUpdateClientExePath());

                _logger.ProgressInfo("NzbDrone will restart shortly.");

                _processProvider.Start(_appFolderInfo.GetUpdateClientExePath(), _processProvider.GetCurrentProcess().Id.ToString());
            }
            catch (Exception ex)
            {
                _logger.ErrorException("Update process failed", ex);
            }
        }
Example #18
0
        public OsVersionModel Read()
        {
            var version = "10.0";

            if (!_diskProvider.FolderExists(PLIST_DIR))
            {
                _logger.Debug("Directory {0} doesn't exist", PLIST_DIR);
                return(null);
            }

            var allFiles = _diskProvider.GetFiles(PLIST_DIR, SearchOption.TopDirectoryOnly);

            var versionFile = allFiles.SingleOrDefault(c =>
                                                       c.EndsWith("SystemVersion.plist") ||
                                                       c.EndsWith("ServerVersion.plist")
                                                       );

            if (string.IsNullOrWhiteSpace(versionFile))
            {
                _logger.Debug("Couldn't find version plist file in {0}", PLIST_DIR);
                return(null);
            }

            var text  = _diskProvider.ReadAllText(versionFile);
            var match = DarwinVersionRegex.Match(text);



            if (match.Success)
            {
                version = match.Groups["version"].Value;
            }

            var name = versionFile.Contains("Server") ? "macOS Server" : "macOS";

            return(new OsVersionModel(name, version));
        }
Example #19
0
        public TransferMode TransferFolder(string sourcePath, string targetPath, TransferMode mode, bool verified = true)
        {
            Ensure.That(sourcePath, () => sourcePath).IsValidPath();
            Ensure.That(targetPath, () => targetPath).IsValidPath();

            if (VerificationMode != DiskTransferVerificationMode.Transactional)
            {
                verified = false;
            }

            if (!_diskProvider.FolderExists(targetPath))
            {
                _diskProvider.CreateFolder(targetPath);
            }

            var result = mode;

            foreach (var subDir in _diskProvider.GetDirectoryInfos(sourcePath))
            {
                result &= TransferFolder(subDir.FullName, Path.Combine(targetPath, subDir.Name), mode, verified);
            }

            foreach (var sourceFile in _diskProvider.GetFileInfos(sourcePath))
            {
                var destFile = Path.Combine(targetPath, sourceFile.Name);

                result &= TransferFile(sourceFile.FullName, destFile, mode, true, verified);
            }

            if (mode.HasFlag(TransferMode.Move))
            {
                _diskProvider.DeleteFolder(sourcePath, true);
            }

            return(result);
        }
Example #20
0
        public void Backup()
        {
            _logger.Info("Backing up appdata (database/config)");
            var backupFolderAppData = _appFolderInfo.GetUpdateBackUpAppDataFolder();

            if (_diskProvider.FolderExists(backupFolderAppData))
            {
                _diskProvider.EmptyFolder(backupFolderAppData);
            }
            else
            {
                _diskProvider.CreateFolder(backupFolderAppData);
            }

            try
            {
                _diskTransferService.TransferFile(_appFolderInfo.GetConfigPath(), _appFolderInfo.GetUpdateBackupConfigFile(), TransferMode.Copy);
                _diskTransferService.TransferFile(_appFolderInfo.GetDatabase(), _appFolderInfo.GetUpdateBackupDatabase(), TransferMode.Copy);
            }
            catch (Exception e)
            {
                _logger.Error(e, "Couldn't create a data backup");
            }
        }
Example #21
0
        private bool InstallUpdate(UpdatePackage updatePackage)
        {
            EnsureAppDataSafety();

            if (OsInfo.IsWindows || _configFileProvider.UpdateMechanism != UpdateMechanism.Script)
            {
                var startupFolder = _appFolderInfo.StartUpFolder;
                var uiFolder      = Path.Combine(startupFolder, "UI");

                if (!_diskProvider.FolderWritable(startupFolder))
                {
                    throw new UpdateFolderNotWritableException("Cannot install update because startup folder '{0}' is not writable by the user '{1}'.", startupFolder, Environment.UserName);
                }

                if (!_diskProvider.FolderWritable(uiFolder))
                {
                    throw new UpdateFolderNotWritableException("Cannot install update because UI folder '{0}' is not writable by the user '{1}'.", uiFolder, Environment.UserName);
                }
            }

            if (_appFolderInfo.StartUpFolder.EndsWith("_output"))
            {
                _logger.ProgressDebug("Running in developer environment, not updating.");
                return(false);
            }

            var updateSandboxFolder = _appFolderInfo.GetUpdateSandboxFolder();

            var packageDestination = Path.Combine(updateSandboxFolder, updatePackage.FileName);

            if (_diskProvider.FolderExists(updateSandboxFolder))
            {
                _logger.Info("Deleting old update files");
                _diskProvider.DeleteFolder(updateSandboxFolder, true);
            }

            _logger.ProgressInfo("Downloading update {0}", updatePackage.Version);
            _logger.Debug("Downloading update package from [{0}] to [{1}]", updatePackage.Url, packageDestination);
            _httpClient.DownloadFile(updatePackage.Url, packageDestination);

            _logger.ProgressInfo("Verifying update package");

            if (!_updateVerifier.Verify(updatePackage, packageDestination))
            {
                _logger.Error("Update package is invalid");
                throw new UpdateVerificationFailedException("Update file '{0}' is invalid", packageDestination);
            }

            _logger.Info("Update package verified successfully");

            _logger.ProgressInfo("Extracting Update package");
            _archiveService.Extract(packageDestination, updateSandboxFolder);
            _logger.Info("Update package extracted successfully");

            EnsureValidBranch(updatePackage);

            _backupService.Backup(BackupType.Update);

            if (OsInfo.IsNotWindows && _configFileProvider.UpdateMechanism == UpdateMechanism.Script)
            {
                InstallUpdateWithScript(updateSandboxFolder);
                return(true);
            }

            _logger.Info("Preparing client");
            _diskTransferService.TransferFolder(_appFolderInfo.GetUpdateClientFolder(), updateSandboxFolder, TransferMode.Move);

            _logger.Info("Starting update client {0}", _appFolderInfo.GetUpdateClientExePath());
            _logger.ProgressInfo("Sonarr will restart shortly.");

            _processProvider.Start(_appFolderInfo.GetUpdateClientExePath(), GetUpdaterArgs(updateSandboxFolder));

            return(true);
        }
Example #22
0
        public void Handle(SeriesScannedEvent message)
        {
            if (!_diskProvider.FolderExists(message.Series.Path))
            {
                return;
            }

            _logger.Debug("Looking for existing metadata in {0}", message.Series.Path);

            var filesOnDisk = _diskProvider.GetFiles(message.Series.Path, SearchOption.AllDirectories);

            var possibleMetadataFiles = filesOnDisk.Where(c => !MediaFileExtensions.Extensions.Contains(Path.GetExtension(c).ToLower()) &&
                                                          !c.StartsWith(Path.Combine(message.Series.Path, "EXTRAS"))).ToList();

            var filteredFiles = _metadataFileService.FilterExistingFiles(possibleMetadataFiles, message.Series);

            var metadataFiles = new List <MetadataFile>();

            foreach (var possibleMetadataFile in filteredFiles)
            {
                foreach (var consumer in _consumers)
                {
                    var metadata = consumer.FindMetadataFile(message.Series, possibleMetadataFile);

                    if (metadata == null)
                    {
                        continue;
                    }

                    if (metadata.Type == MetadataType.EpisodeImage ||
                        metadata.Type == MetadataType.EpisodeMetadata)
                    {
                        try
                        {
                            var localEpisode = _parsingService.GetLocalEpisode(possibleMetadataFile, message.Series, false);

                            if (localEpisode == null)
                            {
                                _logger.Debug("Unable to parse meta data file: {0}", possibleMetadataFile);
                                break;
                            }

                            if (localEpisode.Episodes.DistinctBy(e => e.EpisodeFileId).Count() > 1)
                            {
                                _logger.Debug("Metadata file: {0} does not match existing files.", possibleMetadataFile);
                                break;
                            }

                            metadata.EpisodeFileId = localEpisode.Episodes.First().EpisodeFileId;
                        }
                        catch (EpisodeNotFoundException e)
                        {
                            _logger.Debug("Cannot find related episodes for: {0}", possibleMetadataFile);
                            continue;
                        }
                    }

                    metadataFiles.Add(metadata);
                }
            }

            _metadataFileService.Upsert(metadataFiles);
        }
        private MovieFile TransferFile(MovieFile movieFile, Movie movie, string destinationFilePath, TransferMode mode)
        {
            Ensure.That(movieFile, () => movieFile).IsNotNull();
            Ensure.That(movie, () => movie).IsNotNull();
            Ensure.That(destinationFilePath, () => destinationFilePath).IsValidPath();

            var movieFilePath = movieFile.Path ?? Path.Combine(movie.Path, movieFile.RelativePath);

            if (!_diskProvider.FileExists(movieFilePath))
            {
                throw new FileNotFoundException("Movie file path does not exist", movieFilePath);
            }

            if (movieFilePath == destinationFilePath)
            {
                throw new SameFilenameException("File not moved, source and destination are the same", movieFilePath);
            }

            _diskTransferService.TransferFile(movieFilePath, destinationFilePath, mode);

            var oldMoviePath = movie.Path;

            var newMoviePath = new OsPath(destinationFilePath).Directory.FullPath.TrimEnd(Path.DirectorySeparatorChar);

            movie.Path = newMoviePath; //We update it when everything went well!

            movieFile.RelativePath = movie.Path.GetRelativePath(destinationFilePath);

            _updateMovieFileService.ChangeFileDateForFile(movieFile, movie);

            try
            {
                _mediaFileAttributeService.SetFolderLastWriteTime(movie.Path, movieFile.DateAdded);
            }

            catch (Exception ex)
            {
                _logger.Warn(ex, "Unable to set last write time");
            }

            _mediaFileAttributeService.SetFilePermissions(destinationFilePath);

            if (oldMoviePath != newMoviePath && _diskProvider.FolderExists(oldMoviePath))
            {
                //Let's move the old files before deleting the old folder. We could just do move folder, but the main file (movie file) is already moved, so eh.
                var files = _diskProvider.GetFiles(oldMoviePath, SearchOption.AllDirectories);

                foreach (var file in files)
                {
                    try
                    {
                        var destFile = Path.Combine(newMoviePath, oldMoviePath.GetRelativePath(file));
                        _diskProvider.EnsureFolder(Path.GetDirectoryName(destFile));
                        _diskProvider.MoveFile(file, destFile);
                    }
                    catch (Exception e)
                    {
                        _logger.Warn(e, "Error while trying to move extra file {0} to new folder. Maybe it already exists? (Manual cleanup necessary!).", oldMoviePath.GetRelativePath(file));
                    }
                }

                if (_diskProvider.GetFiles(oldMoviePath, SearchOption.AllDirectories).Count() == 0)
                {
                    _recycleBinProvider.DeleteFolder(oldMoviePath);
                }
            }

            //Only update the movie path if we were successfull!
            if (oldMoviePath != newMoviePath)
            {
                _movieService.UpdateMovie(movie);
            }

            return(movieFile);
        }
Example #24
0
        public void Execute(ManualImportCommand message)
        {
            _logger.ProgressTrace("Manually importing {0} files using mode {1}", message.Files.Count, message.ImportMode);

            var imported = new List<ImportResult>();
            var importedTrackedDownload = new List<ManuallyImportedFile>();

            for (int i = 0; i < message.Files.Count; i++)
            {
                _logger.ProgressTrace("Processing file {0} of {1}", i + 1, message.Files.Count);

                var file = message.Files[i];
                var movie = _movieService.GetMovie(file.MovieId);
                var fileMovieInfo = Parser.Parser.ParseMoviePath(file.Path) ?? new ParsedMovieInfo();
                var existingFile = movie.Path.IsParentPath(file.Path);
                TrackedDownload trackedDownload = null;

                var localMovie = new LocalMovie
                {
                    ExistingFile = false,
                    FileMovieInfo = fileMovieInfo,
                    Path = file.Path,
                    Quality = file.Quality,
                    Languages = file.Languages,
                    Movie = movie,
                    Size = 0
                };

                if (file.DownloadId.IsNotNullOrWhiteSpace())
                {
                    trackedDownload = _trackedDownloadService.Find(file.DownloadId);
                    localMovie.DownloadClientMovieInfo = trackedDownload?.RemoteMovie?.ParsedMovieInfo;
                }

                if (file.FolderName.IsNotNullOrWhiteSpace())
                {
                    localMovie.FolderMovieInfo = Parser.Parser.ParseMovieTitle(file.FolderName);
                    localMovie.SceneSource = !existingFile;
                }

                localMovie = _aggregationService.Augment(localMovie, trackedDownload?.DownloadItem, false);

                // Apply the user-chosen values.
                localMovie.Movie = movie;
                localMovie.Quality = file.Quality;
                localMovie.Languages = file.Languages;

                //TODO: Cleanup non-tracked downloads
                var importDecision = new ImportDecision(localMovie);

                if (trackedDownload == null)
                {
                    imported.AddRange(_importApprovedMovie.Import(new List<ImportDecision> { importDecision }, !existingFile, null, message.ImportMode));
                }
                else
                {
                    var importResult = _importApprovedMovie.Import(new List<ImportDecision> { importDecision }, true, trackedDownload.DownloadItem, message.ImportMode).First();

                    imported.Add(importResult);

                    importedTrackedDownload.Add(new ManuallyImportedFile
                    {
                        TrackedDownload = trackedDownload,
                        ImportResult = importResult
                    });
                }
            }

            _logger.ProgressTrace("Manually imported {0} files", imported.Count);

            foreach (var groupedTrackedDownload in importedTrackedDownload.GroupBy(i => i.TrackedDownload.DownloadItem.DownloadId).ToList())
            {
                var trackedDownload = groupedTrackedDownload.First().TrackedDownload;

                var importMovie = groupedTrackedDownload.First().ImportResult.ImportDecision.LocalMovie.Movie;
                var outputPath = trackedDownload.ImportItem.OutputPath.FullPath;

                if (_diskProvider.FolderExists(outputPath))
                {
                    if (_downloadedMovieImportService.ShouldDeleteFolder(
                            new DirectoryInfo(outputPath),
                            importMovie) && trackedDownload.DownloadItem.CanMoveFiles)
                    {
                        _diskProvider.DeleteFolder(outputPath, true);
                    }
                }

                if (groupedTrackedDownload.Select(c => c.ImportResult).Any(c => c.Result == ImportResultType.Imported))
                {
                    trackedDownload.State = TrackedDownloadState.Imported;
                    _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload, importMovie.Id));
                }
            }
        }
Example #25
0
        public void Scan(Series series)
        {
            var rootFolder = _rootFolderService.GetBestRootFolderPath(series.Path);

            var seriesFolderExists = _diskProvider.FolderExists(series.Path);

            if (!seriesFolderExists)
            {
                if (!_diskProvider.FolderExists(rootFolder))
                {
                    _logger.Warn("Series' root folder ({0}) doesn't exist.", rootFolder);
                    _eventAggregator.PublishEvent(new SeriesScanSkippedEvent(series, SeriesScanSkippedReason.RootFolderDoesNotExist));
                    return;
                }

                if (_diskProvider.FolderEmpty(rootFolder))
                {
                    _logger.Warn("Series' root folder ({0}) is empty.", rootFolder);
                    _eventAggregator.PublishEvent(new SeriesScanSkippedEvent(series, SeriesScanSkippedReason.RootFolderIsEmpty));
                    return;
                }
            }

            _logger.ProgressInfo("Scanning {0}", series.Title);

            if (!seriesFolderExists)
            {
                if (_configService.CreateEmptySeriesFolders)
                {
                    if (_configService.DeleteEmptyFolders)
                    {
                        _logger.Debug("Not creating missing series folder: {0} because delete empty series folders is enabled", series.Path);
                    }
                    else
                    {
                        _logger.Debug("Creating missing series folder: {0}", series.Path);

                        _diskProvider.CreateFolder(series.Path);
                        SetPermissions(series.Path);
                    }
                }
                else
                {
                    _logger.Debug("Series folder doesn't exist: {0}", series.Path);
                }

                CleanMediaFiles(series, new List <string>());
                CompletedScanning(series);

                return;
            }

            var videoFilesStopwatch = Stopwatch.StartNew();
            var mediaFileList       = FilterPaths(series.Path, GetVideoFiles(series.Path)).ToList();

            videoFilesStopwatch.Stop();
            _logger.Trace("Finished getting episode files for: {0} [{1}]", series, videoFilesStopwatch.Elapsed);

            CleanMediaFiles(series, mediaFileList);

            var decisionsStopwatch = Stopwatch.StartNew();
            var decisions          = _importDecisionMaker.GetImportDecisions(mediaFileList, series);

            decisionsStopwatch.Stop();
            _logger.Trace("Import decisions complete for: {0} [{1}]", series, decisionsStopwatch.Elapsed);
            _importApprovedEpisodes.Import(decisions, false);

            RemoveEmptySeriesFolder(series.Path);
            CompletedScanning(series);
        }
Example #26
0
        public void Scan(Artist artist, FilterFilesType filter = FilterFilesType.Known)
        {
            var rootFolder = _rootFolderService.GetBestRootFolderPath(artist.Path);

            if (!_diskProvider.FolderExists(rootFolder))
            {
                _logger.Warn("Artist' root folder ({0}) doesn't exist.", rootFolder);
                _eventAggregator.PublishEvent(new ArtistScanSkippedEvent(artist, ArtistScanSkippedReason.RootFolderDoesNotExist));
                return;
            }

            if (_diskProvider.GetDirectories(rootFolder).Empty())
            {
                _logger.Warn("Artist' root folder ({0}) is empty.", rootFolder);
                _eventAggregator.PublishEvent(new ArtistScanSkippedEvent(artist, ArtistScanSkippedReason.RootFolderIsEmpty));
                return;
            }

            _logger.ProgressInfo("Scanning {0}", artist.Name);

            if (!_diskProvider.FolderExists(artist.Path))
            {
                if (_configService.CreateEmptyArtistFolders)
                {
                    _logger.Debug("Creating missing artist folder: {0}", artist.Path);
                    _diskProvider.CreateFolder(artist.Path);
                    SetPermissions(artist.Path);
                }
                else
                {
                    _logger.Debug("Artist folder doesn't exist: {0}", artist.Path);
                }

                CleanMediaFiles(artist, new List <string>());
                CompletedScanning(artist);

                return;
            }

            var musicFilesStopwatch = Stopwatch.StartNew();
            var mediaFileList       = FilterFiles(artist.Path, GetAudioFiles(artist.Path)).ToList();

            musicFilesStopwatch.Stop();
            _logger.Trace("Finished getting track files for: {0} [{1}]", artist, musicFilesStopwatch.Elapsed);

            CleanMediaFiles(artist, mediaFileList.Select(x => x.FullName).ToList());

            var decisionsStopwatch = Stopwatch.StartNew();
            var decisions          = _importDecisionMaker.GetImportDecisions(mediaFileList, artist, filter, true);

            decisionsStopwatch.Stop();
            _logger.Debug("Import decisions complete for: {0} [{1}]", artist, decisionsStopwatch.Elapsed);

            var importStopwatch = Stopwatch.StartNew();

            _importApprovedTracks.Import(decisions, false);

            // decisions may have been filtered to just new files.  Anything new and approved will have been inserted.
            // Now we need to make sure anything new but not approved gets inserted
            // Note that knownFiles will include anything imported just now
            var knownFiles = _mediaFileService.GetFilesWithBasePath(artist.Path);

            var newFiles = decisions
                           .ExceptBy(x => x.Item.Path, knownFiles, x => x.Path, PathEqualityComparer.Instance)
                           .Select(decision => new TrackFile {
                Path      = decision.Item.Path,
                Size      = decision.Item.Size,
                Modified  = decision.Item.Modified,
                DateAdded = DateTime.UtcNow,
                Quality   = decision.Item.Quality,
                MediaInfo = decision.Item.FileTrackInfo.MediaInfo
            })
                           .ToList();

            _mediaFileService.AddMany(newFiles);

            _logger.Debug($"Inserted {newFiles.Count} new unmatched trackfiles");

            // finally update info on size/modified for existing files
            var updatedFiles = knownFiles
                               .Join(decisions,
                                     x => x.Path,
                                     x => x.Item.Path,
                                     (file, decision) => new {
                File = file,
                Item = decision.Item
            },
                                     PathEqualityComparer.Instance)
                               .Where(x => x.File.Size != x.Item.Size ||
                                      Math.Abs((x.File.Modified - x.Item.Modified).TotalSeconds) > 1)
                               .Select(x => {
                x.File.Size      = x.Item.Size;
                x.File.Modified  = x.Item.Modified;
                x.File.MediaInfo = x.Item.FileTrackInfo.MediaInfo;
                x.File.Quality   = x.Item.Quality;
                return(x.File);
            })
                               .ToList();

            _mediaFileService.Update(updatedFiles);

            _logger.Debug($"Updated info for {updatedFiles.Count} known files");

            RemoveEmptyArtistFolder(artist.Path);

            CompletedScanning(artist);
            importStopwatch.Stop();
            _logger.Debug("Track import complete for: {0} [{1}]", artist, importStopwatch.Elapsed);
        }
        public override HealthCheck Check()
        {
            // We don't care about client folders if we are not handling completed files
            if (!_configService.EnableCompletedDownloadHandling)
            {
                return(new HealthCheck(GetType()));
            }

            var clients = _downloadClientProvider.GetDownloadClients();

            foreach (var client in clients)
            {
                try
                {
                    var status  = client.GetStatus();
                    var folders = status.OutputRootFolders;
                    foreach (var folder in folders)
                    {
                        if (!folder.IsValid)
                        {
                            if (!status.IsLocalhost)
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckWrongOSPath"), client.Definition.Name, folder.FullPath, _osInfo.Name), "#bad_remote_path_mapping"));
                            }
                            else if (_osInfo.IsDocker)
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckBadDockerPath"), client.Definition.Name, folder.FullPath, _osInfo.Name), "#docker_bad_remote_path_mapping"));
                            }
                            else
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckLocalWrongOSPath"), client.Definition.Name, folder.FullPath, _osInfo.Name), "#bad_download_client_settings"));
                            }
                        }

                        if (!_diskProvider.FolderExists(folder.FullPath))
                        {
                            if (_osInfo.IsDocker)
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckDockerFolderMissing"), client.Definition.Name, folder.FullPath), "#docker_bad_remote_path_mapping"));
                            }
                            else if (!status.IsLocalhost)
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckLocalFolderMissing"), client.Definition.Name, folder.FullPath), "#bad_remote_path_mapping"));
                            }
                            else
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckGenericPermissions"), client.Definition.Name, folder.FullPath), "#permissions_error"));
                            }
                        }
                    }
                }
                catch (DownloadClientException ex)
                {
                    _logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, "Unknown error occured in RemotePathMapping HealthCheck");
                }
            }

            return(new HealthCheck(GetType()));
        }
Example #28
0
        public void Scan(List <string> folders = null, FilterFilesType filter = FilterFilesType.Known, bool addNewAuthors = false, List <int> authorIds = null)
        {
            if (folders == null)
            {
                folders = _rootFolderService.All().Select(x => x.Path).ToList();
            }

            if (authorIds == null)
            {
                authorIds = new List <int>();
            }

            var mediaFileList = new List <IFileInfo>();

            var musicFilesStopwatch = Stopwatch.StartNew();

            foreach (var folder in folders)
            {
                // We could be scanning a root folder or a subset of a root folder.  If it's a subset,
                // check if the root folder exists before cleaning.
                var rootFolder = _rootFolderService.GetBestRootFolder(folder);

                if (rootFolder == null)
                {
                    _logger.Error("Not scanning {0}, it's not a subdirectory of a defined root folder", folder);
                    return;
                }

                var folderExists = _diskProvider.FolderExists(folder);

                if (!folderExists)
                {
                    if (!_diskProvider.FolderExists(rootFolder.Path))
                    {
                        _logger.Warn("Authors' root folder ({0}) doesn't exist.", rootFolder);
                        var skippedAuthors = _authorService.GetAuthors(authorIds);
                        skippedAuthors.ForEach(x => _eventAggregator.PublishEvent(new AuthorScanSkippedEvent(x, AuthorScanSkippedReason.RootFolderDoesNotExist)));
                        return;
                    }

                    if (_diskProvider.FolderEmpty(rootFolder.Path))
                    {
                        _logger.Warn("Authors' root folder ({0}) is empty.", rootFolder);
                        var skippedAuthors = _authorService.GetAuthors(authorIds);
                        skippedAuthors.ForEach(x => _eventAggregator.PublishEvent(new AuthorScanSkippedEvent(x, AuthorScanSkippedReason.RootFolderIsEmpty)));
                        return;
                    }
                }

                if (!folderExists)
                {
                    _logger.Debug("Specified scan folder ({0}) doesn't exist.", folder);

                    CleanMediaFiles(folder, new List <string>());
                    continue;
                }

                _logger.ProgressInfo("Scanning {0}", folder);

                var files = FilterFiles(folder, GetBookFiles(folder));

                if (!files.Any())
                {
                    _logger.Warn("Scan folder {0} is empty.", folder);
                    continue;
                }

                CleanMediaFiles(folder, files.Select(x => x.FullName).ToList());
                mediaFileList.AddRange(files);
            }

            musicFilesStopwatch.Stop();
            _logger.Trace("Finished getting track files for:\n{0} [{1}]", folders.ConcatToString("\n"), musicFilesStopwatch.Elapsed);

            var decisionsStopwatch = Stopwatch.StartNew();

            var config = new ImportDecisionMakerConfig
            {
                Filter          = filter,
                IncludeExisting = true,
                AddNewAuthors   = addNewAuthors
            };

            var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, null, null, config);

            decisionsStopwatch.Stop();
            _logger.Debug("Import decisions complete [{0}]", decisionsStopwatch.Elapsed);

            var importStopwatch = Stopwatch.StartNew();

            _importApprovedTracks.Import(decisions, false);

            // decisions may have been filtered to just new files.  Anything new and approved will have been inserted.
            // Now we need to make sure anything new but not approved gets inserted
            // Note that knownFiles will include anything imported just now
            var knownFiles = new List <BookFile>();

            folders.ForEach(x => knownFiles.AddRange(_mediaFileService.GetFilesWithBasePath(x)));

            var newFiles = decisions
                           .ExceptBy(x => x.Item.Path, knownFiles, x => x.Path, PathEqualityComparer.Instance)
                           .Select(decision => new BookFile
            {
                Path      = decision.Item.Path,
                CalibreId = decision.Item.Path.ParseCalibreId(),
                Size      = decision.Item.Size,
                Modified  = decision.Item.Modified,
                DateAdded = DateTime.UtcNow,
                Quality   = decision.Item.Quality,
                MediaInfo = decision.Item.FileTrackInfo.MediaInfo,
                Edition   = decision.Item.Edition
            })
                           .ToList();

            _mediaFileService.AddMany(newFiles);

            _logger.Debug($"Inserted {newFiles.Count} new unmatched trackfiles");

            // finally update info on size/modified for existing files
            var updatedFiles = knownFiles
                               .Join(decisions,
                                     x => x.Path,
                                     x => x.Item.Path,
                                     (file, decision) => new
            {
                File = file,
                Item = decision.Item
            },
                                     PathEqualityComparer.Instance)
                               .Where(x => x.File.Size != x.Item.Size ||
                                      Math.Abs((x.File.Modified - x.Item.Modified).TotalSeconds) > 1)
                               .Select(x =>
            {
                x.File.Size      = x.Item.Size;
                x.File.Modified  = x.Item.Modified;
                x.File.MediaInfo = x.Item.FileTrackInfo.MediaInfo;
                x.File.Quality   = x.Item.Quality;
                return(x.File);
            })
                               .ToList();

            _mediaFileService.Update(updatedFiles);

            _logger.Debug($"Updated info for {updatedFiles.Count} known files");

            var authors = _authorService.GetAuthors(authorIds);

            foreach (var author in authors)
            {
                CompletedScanning(author);
            }

            importStopwatch.Stop();
            _logger.Debug("Book import complete for:\n{0} [{1}]", folders.ConcatToString("\n"), importStopwatch.Elapsed);
        }
Example #29
0
        public override HealthCheck Check()
        {
            // We don't care about client folders if we are not handling completed files
            if (!_configService.EnableCompletedDownloadHandling)
            {
                return(new HealthCheck(GetType()));
            }

            var clients = _downloadClientProvider.GetDownloadClients();

            foreach (var client in clients)
            {
                try
                {
                    var status  = client.GetStatus();
                    var folders = status.OutputRootFolders;
                    if (folders != null)
                    {
                        foreach (var folder in folders)
                        {
                            if (!folder.IsValid)
                            {
                                if (!status.IsLocalhost)
                                {
                                    return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote download client {client.Definition.Name} places downloads in {folder.FullPath} but this is not a valid {_osInfo.Name} path.  Review your remote path mappings and download client settings.", "#bad_remote_path_mapping"));
                                }
                                else if (_osInfo.IsDocker)
                                {
                                    return(new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; download client {client.Definition.Name} places downloads in {folder.FullPath} but this is not a valid {_osInfo.Name} path.  Review your remote path mappings and download client settings.", "#docker_bad_remote_path_mapping"));
                                }
                                else
                                {
                                    return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Local download client {client.Definition.Name} places downloads in {folder.FullPath} but this is not a valid {_osInfo.Name} path.  Review your download client settings.", "#bad_download_client_settings"));
                                }
                            }

                            if (!_diskProvider.FolderExists(folder.FullPath))
                            {
                                if (_osInfo.IsDocker)
                                {
                                    return(new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; download client {client.Definition.Name} places downloads in {folder.FullPath} but this directory does not appear to exist inside the container.  Review your remote path mappings and container volume settings.", "#docker_bad_remote_path_mapping"));
                                }
                                else if (!status.IsLocalhost)
                                {
                                    return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote download client {client.Definition.Name} places downloads in {folder.FullPath} but this directory does not appear to exist.  Likely missing or incorrect remote path mapping.", "#bad_remote_path_mapping"));
                                }
                                else
                                {
                                    return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Download client {client.Definition.Name} places downloads in {folder.FullPath} but Lidarr cannot see this directory.  You may need to adjust the folder's permissions.", "#permissions_error"));
                                }
                            }
                        }
                    }
                }
                catch (DownloadClientException ex)
                {
                    _logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, "Unknown error occured in RemotePathMapping HealthCheck");
                }
            }

            return(new HealthCheck(GetType()));
        }
        private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List <Episode> episodes, String destinationFilename, Boolean copyOnly)
        {
            Ensure.That(episodeFile, () => episodeFile).IsNotNull();
            Ensure.That(series, () => series).IsNotNull();
            Ensure.That(destinationFilename, () => destinationFilename).IsValidPath();

            var episodeFilePath = episodeFile.Path ?? Path.Combine(series.Path, episodeFile.RelativePath);

            if (!_diskProvider.FileExists(episodeFilePath))
            {
                throw new FileNotFoundException("Episode file path does not exist", episodeFilePath);
            }

            if (episodeFilePath.PathEquals(destinationFilename))
            {
                throw new SameFilenameException("File not moved, source and destination are the same", episodeFilePath);
            }

            var directoryName = new FileInfo(destinationFilename).DirectoryName;

            if (!_diskProvider.FolderExists(directoryName))
            {
                try
                {
                    _diskProvider.CreateFolder(directoryName);
                }
                catch (IOException ex)
                {
                    _logger.ErrorException("Unable to create directory: " + directoryName, ex);
                }

                SetFolderPermissions(directoryName);

                if (!directoryName.PathEquals(series.Path))
                {
                    SetFolderPermissions(series.Path);
                }
            }

            if (copyOnly)
            {
                _logger.Debug("Copying [{0}] > [{1}]", episodeFilePath, destinationFilename);
                _diskProvider.CopyFile(episodeFilePath, destinationFilename);
            }
            else
            {
                _logger.Debug("Moving [{0}] > [{1}]", episodeFilePath, destinationFilename);
                _diskProvider.MoveFile(episodeFilePath, destinationFilename);
            }

            episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilename);

            _updateEpisodeFileService.ChangeFileDateForFile(episodeFile, series, episodes);

            try
            {
                SetFolderLastWriteTime(series.Path, episodeFile.DateAdded);

                if (series.SeasonFolder)
                {
                    var seasonFolder = Path.GetDirectoryName(destinationFilename);

                    SetFolderLastWriteTime(seasonFolder, episodeFile.DateAdded);
                }
            }

            catch (Exception ex)
            {
                _logger.WarnException("Unable to set last write time", ex);
            }

            //We should only run this on Windows
            if (OsInfo.IsWindows)
            {
                //Wrapped in Try/Catch to prevent this from causing issues with remote NAS boxes, the move worked, which is more important.
                try
                {
                    _diskProvider.InheritFolderPermissions(destinationFilename);
                }
                catch (Exception ex)
                {
                    if (ex is UnauthorizedAccessException || ex is InvalidOperationException)
                    {
                        _logger.Debug("Unable to apply folder permissions to: ", destinationFilename);
                        _logger.DebugException(ex.Message, ex);
                    }

                    else
                    {
                        throw;
                    }
                }
            }

            else
            {
                SetPermissions(destinationFilename, _configService.FileChmod);
            }

            return(episodeFile);
        }