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 void Scan(Series series) { var rootFolder = _diskProvider.GetParentFolder(series.Path); if (!_diskProvider.FolderExists(rootFolder)) { _logger.Warn("Series' root folder ({0}) doesn't exist.", rootFolder); _eventAggregator.PublishEvent(new SeriesScanSkippedEvent(series, SeriesScanSkippedReason.RootFolderDoesNotExist)); return; } if (_diskProvider.GetDirectories(rootFolder).Empty()) { _logger.Warn("Series' root folder ({0}) is empty.", rootFolder); _eventAggregator.PublishEvent(new SeriesScanSkippedEvent(series, SeriesScanSkippedReason.RootFolderIsEmpty)); return; } _logger.ProgressInfo("Scanning disk for {0}", series.Title); if (!_diskProvider.FolderExists(series.Path)) { if (_configService.CreateEmptySeriesFolders) { _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 = FilterFiles(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); }
private void RemoveExistingTrackFiles(Artist artist, Album album) { var rootFolder = _diskProvider.GetParentFolder(artist.Path); var previousFiles = _mediaFileService.GetFilesByAlbum(album.Id); _logger.Debug($"Deleting {previousFiles.Count} existing files for {album}"); foreach (var previousFile in previousFiles) { var subfolder = rootFolder.GetRelativePath(_diskProvider.GetParentFolder(previousFile.Path)); if (_diskProvider.FileExists(previousFile.Path)) { _logger.Debug("Removing existing track file: {0}", previousFile); _recycleBinProvider.DeleteFile(previousFile.Path, subfolder); } _mediaFileService.Delete(previousFile, DeleteMediaFileReason.Upgrade); } }
private void RemoveExistingTrackFiles(Author author, Book book) { var rootFolder = _diskProvider.GetParentFolder(author.Path); var previousFiles = _mediaFileService.GetFilesByBook(book.Id); _logger.Debug($"Deleting {previousFiles.Count} existing files for {book}"); foreach (var previousFile in previousFiles) { var subfolder = rootFolder.GetRelativePath(_diskProvider.GetParentFolder(previousFile.Path)); if (_diskProvider.FileExists(previousFile.Path)) { _logger.Debug("Removing existing book file: {0}", previousFile); _recycleBinProvider.DeleteFile(previousFile.Path, subfolder); } _mediaFileService.Delete(previousFile, DeleteMediaFileReason.Upgrade); } }
private void DeleteEpisodeFile(int id) { var episodeFile = _mediaFileService.Get(id); var series = _seriesService.GetSeries(episodeFile.SeriesId); var fullPath = Path.Combine(series.Path, episodeFile.RelativePath); var subfolder = _diskProvider.GetParentFolder(series.Path).GetRelativePath(_diskProvider.GetParentFolder(fullPath)); _logger.Info("Deleting episode file: {0}", fullPath); _recycleBinProvider.DeleteFile(fullPath, subfolder); _mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual); }
public string GetBestRootFolderPath(string path) { var possibleRootFolder = GetBestRootFolder(path); if (possibleRootFolder == null) { return(_diskProvider.GetParentFolder(path)); } return(possibleRootFolder.Path); }
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); }
public void ImportExtraFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, bool isReadOnly) { var series = localEpisode.Series; foreach (var extraFileManager in _extraFileManagers) { extraFileManager.CreateAfterEpisodeImport(series, episodeFile); } if (!_configService.ImportExtraFiles) { return; } var sourcePath = localEpisode.Path; var sourceFolder = _diskProvider.GetParentFolder(sourcePath); var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath); var files = _diskProvider.GetFiles(sourceFolder, SearchOption.TopDirectoryOnly); var wantedExtensions = _configService.ExtraFileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(e => e.Trim(' ', '.')) .ToList(); var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName)); foreach (var matchingFilename in matchingFilenames) { var matchingExtension = wantedExtensions.FirstOrDefault(e => matchingFilename.EndsWith(e)); if (matchingExtension == null) { continue; } try { foreach (var extraFileManager in _extraFileManagers) { var extension = Path.GetExtension(matchingFilename); var extraFile = extraFileManager.Import(series, episodeFile, matchingFilename, extension, isReadOnly); if (extraFile != null) { break; } } } catch (Exception ex) { _logger.Warn(ex, "Failed to import extra file: {0}", matchingFilename); } } }
public void Scan(Series series) { var rootFolder = _diskProvider.GetParentFolder(series.Path); if (!_diskProvider.FolderExists(rootFolder)) { _logger.Warn("Series' root folder ({0}) doesn't exist.", rootFolder); return; } if (_diskProvider.GetDirectories(rootFolder).Empty()) { _logger.Warn("Series' root folder ({0}) is empty.", rootFolder); return; } _logger.ProgressInfo("Scanning disk for {0}", series.Title); _commandExecutor.PublishCommand(new CleanMediaFileDb(series.Id)); if (!_diskProvider.FolderExists(series.Path)) { if (_configService.CreateEmptySeriesFolders && _diskProvider.FolderExists(rootFolder)) { _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); } return; } var videoFilesStopwatch = Stopwatch.StartNew(); var mediaFileList = GetVideoFiles(series.Path).Where(file => !ExcludedSubFoldersRegex.IsMatch(series.Path.GetRelativePath(file))).ToList(); videoFilesStopwatch.Stop(); _logger.Trace("Finished getting episode files for: {0} [{1}]", series, videoFilesStopwatch.Elapsed); var decisionsStopwatch = Stopwatch.StartNew(); var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, series, false); decisionsStopwatch.Stop(); _logger.Trace("Import decisions complete for: {0} [{1}]", series, decisionsStopwatch.Elapsed); _importApprovedEpisodes.Import(decisions, false); _logger.Info("Completed scanning disk for {0}", series.Title); _eventAggregator.PublishEvent(new SeriesScannedEvent(series)); }
public void DeleteEpisodeFile(Series series, EpisodeFile episodeFile) { var fullPath = Path.Combine(series.Path, episodeFile.RelativePath); var rootFolder = _diskProvider.GetParentFolder(series.Path); if (!_diskProvider.FolderExists(rootFolder)) { _logger.Warn("Series' root folder ({0}) doesn't exist.", rootFolder); throw new NzbDroneClientException(HttpStatusCode.Conflict, "Series' root folder ({0}) doesn't exist.", rootFolder); } if (_diskProvider.GetDirectories(rootFolder).Empty()) { _logger.Warn("Series' root folder ({0}) is empty.", rootFolder); throw new NzbDroneClientException(HttpStatusCode.Conflict, "Series' root folder ({0}) is empty.", rootFolder); } if (_diskProvider.FolderExists(series.Path) && _diskProvider.FileExists(fullPath)) { _logger.Info("Deleting episode file: {0}", fullPath); var subfolder = _diskProvider.GetParentFolder(series.Path).GetRelativePath(_diskProvider.GetParentFolder(fullPath)); try { _recycleBinProvider.DeleteFile(fullPath, subfolder); } catch (Exception e) { _logger.Error(e, "Unable to delete episode file"); throw new NzbDroneClientException(HttpStatusCode.InternalServerError, "Unable to delete episode file"); } } // Delete the episode file from the database to clean it up even if the file was already deleted _mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual); _eventAggregator.PublishEvent(new DeleteCompletedEvent()); }
public string GetBestRootFolderPath(string path) { var possibleRootFolder = All().Where(r => r.Path.IsParentPath(path)) .OrderByDescending(r => r.Path.Length) .FirstOrDefault(); if (possibleRootFolder == null) { return(_diskProvider.GetParentFolder(path)); } return(possibleRootFolder.Path); }
private void ImportExtraFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, bool isReadOnly) { if (!_configService.ImportExtraFiles) { return; } var folderSearchOption = localEpisode.FolderEpisodeInfo == null ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories; var wantedExtensions = _configService.ExtraFileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(e => e.Trim(' ', '.') .Insert(0, ".")) .ToList(); var sourceFolder = _diskProvider.GetParentFolder(localEpisode.Path); var files = _diskProvider.GetFiles(sourceFolder, folderSearchOption); var managedFiles = _extraFileManagers.Select((i) => new List <string>()).ToArray(); foreach (var file in files) { var extension = Path.GetExtension(file); var matchingExtension = wantedExtensions.FirstOrDefault(e => e.Equals(extension)); if (matchingExtension == null) { continue; } for (int i = 0; i < _extraFileManagers.Count; i++) { if (_extraFileManagers[i].CanImportFile(localEpisode, episodeFile, file, extension, isReadOnly)) { managedFiles[i].Add(file); break; } } } for (int i = 0; i < _extraFileManagers.Count; i++) { _extraFileManagers[i].ImportFiles(localEpisode, episodeFile, managedFiles[i], isReadOnly); } }
public override HealthCheck Check() { var missingRootFolders = _seriesService.GetAllSeries() .Select(s => _diskProvider.GetParentFolder(s.Path)) .Distinct() .Where(s => !_diskProvider.FolderExists(s)) .ToList(); if (missingRootFolders.Any()) { if (missingRootFolders.Count == 1) { return(new HealthCheck(GetType(), HealthCheckResult.Error, "Missing root folder: " + missingRootFolders.First())); } var message = String.Format("Multiple root folders are missing: {0}", String.Join(" | ", missingRootFolders)); return(new HealthCheck(GetType(), HealthCheckResult.Error, message)); } return(new HealthCheck(GetType())); }
public void Scan(Series series) { _logger.ProgressInfo("Scanning disk for {0}", series.Title); _commandExecutor.PublishCommand(new CleanMediaFileDb(series.Id)); if (!_diskProvider.FolderExists(series.Path)) { if (_configService.CreateEmptySeriesFolders && _diskProvider.FolderExists(_diskProvider.GetParentFolder(series.Path))) { _logger.Debug("Creating missing series folder: {0}", series.Path); _diskProvider.CreateFolder(series.Path); } else { _logger.Debug("Series folder doesn't exist: {0}", series.Path); } return; } var videoFilesStopwatch = Stopwatch.StartNew(); var mediaFileList = GetVideoFiles(series.Path).ToList(); videoFilesStopwatch.Stop(); _logger.Trace("Finished getting episode files for: {0} [{1}]", series, videoFilesStopwatch.Elapsed); var decisionsStopwatch = Stopwatch.StartNew(); var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, series, false); decisionsStopwatch.Stop(); _logger.Trace("Import decisions complete for: {0} [{1}]", series, decisionsStopwatch.Elapsed); _importApprovedEpisodes.Import(decisions); _logger.Info("Completed scanning disk for {0}", series.Title); _eventAggregator.PublishEvent(new SeriesScannedEvent(series)); }
private MetadataFile GetMetadataFile(Series series, List <MetadataFile> existingMetadataFiles, Func <MetadataFile, bool> predicate) { var matchingMetadataFiles = existingMetadataFiles.Where(predicate).ToList(); if (matchingMetadataFiles.Empty()) { return(null); } //Remove duplicate metadata files from DB and disk foreach (var file in matchingMetadataFiles.Skip(1)) { var path = Path.Combine(series.Path, file.RelativePath); _logger.Debug("Removing duplicate Metadata file: {0}", path); var subfolder = _diskProvider.GetParentFolder(series.Path).GetRelativePath(_diskProvider.GetParentFolder(path)); _recycleBinProvider.DeleteFile(path, subfolder); _metadataFileService.Delete(file.Id); } return(matchingMetadataFiles.First()); }
public void Handle(EpisodeFileDeletedEvent message) { var episodeFile = message.EpisodeFile; if (message.Reason == DeleteMediaFileReason.NoLinkedEpisodes) { _logger.Debug("Removing episode file from DB as part of cleanup routine, not deleting extra files from disk."); } else { var series = _seriesService.GetSeries(message.EpisodeFile.SeriesId); foreach (var extra in _repository.GetFilesByEpisodeFile(episodeFile.Id)) { var path = Path.Combine(series.Path, extra.RelativePath); if (_diskProvider.FileExists(path)) { if (PermanentlyDelete) { _diskProvider.DeleteFile(path); } else { // Send extra files to the recycling bin so they can be recovered if necessary var subfolder = _diskProvider.GetParentFolder(series.Path).GetRelativePath(_diskProvider.GetParentFolder(path)); _recycleBinProvider.DeleteFile(path, subfolder); } } } } _logger.Debug("Deleting Extra from database for episode file: {0}", episodeFile); _repository.DeleteForEpisodeFile(episodeFile.Id); }
public override IEnumerable <ExtraFile> ImportFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, List <string> files, bool isReadOnly) { var importedFiles = new List <SubtitleFile>(); var filteredFiles = files.Where(f => CanImportFile(localEpisode, episodeFile, f, Path.GetExtension(f), isReadOnly)).ToList(); var sourcePath = localEpisode.Path; var sourceFolder = _diskProvider.GetParentFolder(sourcePath); var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath); var matchingFiles = new List <string>(); foreach (var file in filteredFiles) { try { // Filename match if (Path.GetFileNameWithoutExtension(file).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase)) { matchingFiles.Add(file); continue; } // Season and episode match var fileEpisodeInfo = Parser.Parser.ParsePath(file) ?? new ParsedEpisodeInfo(); if (fileEpisodeInfo.EpisodeNumbers.Length == 0) { continue; } if (fileEpisodeInfo.SeasonNumber == localEpisode.FileEpisodeInfo.SeasonNumber && fileEpisodeInfo.EpisodeNumbers.SequenceEqual(localEpisode.FileEpisodeInfo.EpisodeNumbers)) { matchingFiles.Add(file); } } catch (Exception ex) { _logger.Warn(ex, "Failed to import subtitle file: {0}", file); } } // Use any sub if only episode in folder if (matchingFiles.Count == 0 && filteredFiles.Count > 0) { var videoFiles = _diskProvider.GetFiles(sourceFolder, SearchOption.AllDirectories) .Where(file => MediaFileExtensions.Extensions.Contains(Path.GetExtension(file))) .ToList(); if (videoFiles.Count() > 2) { return(importedFiles); } // Filter out samples videoFiles = videoFiles.Where(file => { var sample = _detectSample.IsSample(localEpisode.Series, file, false); if (sample == DetectSampleResult.Sample) { return(false); } return(true); }).ToList(); if (videoFiles.Count == 1) { matchingFiles.AddRange(filteredFiles); _logger.Warn("Imported any available subtitle file for episode: {0}", localEpisode); } } var subtitleFiles = new List <Tuple <string, Language, string> >(); foreach (string file in matchingFiles) { var language = LanguageParser.ParseSubtitleLanguage(file); var extension = Path.GetExtension(file); subtitleFiles.Add(new Tuple <string, Language, string>(file, language, extension)); } var groupedSubtitleFiles = subtitleFiles.GroupBy(s => s.Item2 + s.Item3).ToList(); foreach (var group in groupedSubtitleFiles) { var groupCount = group.Count(); var copy = 1; foreach (var file in group) { try { var path = file.Item1; var language = file.Item2; var extension = file.Item3; var suffix = GetSuffix(language, copy, groupCount > 1); var subtitleFile = ImportFile(localEpisode.Series, episodeFile, path, isReadOnly, extension, suffix); subtitleFile.Language = language; _mediaFileAttributeService.SetFilePermissions(path); _subtitleFileService.Upsert(subtitleFile); importedFiles.Add(subtitleFile); copy++; } catch (Exception ex) { _logger.Warn(ex, "Failed to import subtitle file: {0}", file.Item1); } } } return(importedFiles); }
public override IEnumerable <ExtraFile> ImportFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, List <string> files, bool isReadOnly) { var importedFiles = new List <ExtraFile>(); var filteredFiles = files.Where(f => CanImportFile(localEpisode, episodeFile, f, Path.GetExtension(f), isReadOnly)).ToList(); var sourcePath = localEpisode.Path; var sourceFolder = _diskProvider.GetParentFolder(sourcePath); var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath); var matchingFiles = new List <string>(); var hasNfo = false; foreach (var file in filteredFiles) { try { // Filter out duplicate NFO files if (file.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase)) { if (hasNfo) { continue; } hasNfo = true; } // Filename match if (Path.GetFileNameWithoutExtension(file).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase)) { matchingFiles.Add(file); continue; } // Season and episode match var fileEpisodeInfo = Parser.Parser.ParsePath(file) ?? new ParsedEpisodeInfo(); if (fileEpisodeInfo.EpisodeNumbers.Length == 0) { continue; } if (fileEpisodeInfo.SeasonNumber == localEpisode.FileEpisodeInfo.SeasonNumber && fileEpisodeInfo.EpisodeNumbers.SequenceEqual(localEpisode.FileEpisodeInfo.EpisodeNumbers)) { matchingFiles.Add(file); } } catch (Exception ex) { _logger.Warn(ex, "Failed to import extra file: {0}", file); } } foreach (string file in matchingFiles) { try { var extraFile = ImportFile(localEpisode.Series, episodeFile, file, isReadOnly, Path.GetExtension(file), null); _mediaFileAttributeService.SetFilePermissions(file); _otherExtraFileService.Upsert(extraFile); importedFiles.Add(extraFile); } catch (Exception ex) { _logger.Warn(ex, "Failed to import extra file: {0}", file); } } return(importedFiles); }
public BookFileMoveResult UpgradeBookFile(BookFile bookFile, LocalBook localBook, bool copyOnly = false) { var moveFileResult = new BookFileMoveResult(); var existingFiles = localBook.Book.BookFiles.Value; var rootFolderPath = _diskProvider.GetParentFolder(localBook.Author.Path); var rootFolder = _rootFolderService.GetBestRootFolder(rootFolderPath); var isCalibre = rootFolder.IsCalibreLibrary && rootFolder.CalibreSettings != null; var settings = rootFolder.CalibreSettings; // If there are existing book 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(rootFolderPath)) { throw new RootFolderNotFoundException($"Root folder '{rootFolderPath}' was not found."); } foreach (var file in existingFiles) { var bookFilePath = file.Path; var subfolder = rootFolderPath.GetRelativePath(_diskProvider.GetParentFolder(bookFilePath)); bookFile.CalibreId = file.CalibreId; if (_diskProvider.FileExists(bookFilePath)) { _logger.Debug("Removing existing book file: {0} CalibreId: {1}", file, file.CalibreId); if (!isCalibre) { _recycleBinProvider.DeleteFile(bookFilePath, subfolder); } else { var existing = _calibre.GetBook(file.CalibreId, settings); var existingFormats = existing.Formats.Keys; _logger.Debug($"Removing existing formats {existingFormats.ConcatToString()} from calibre"); _calibre.RemoveFormats(file.CalibreId, existingFormats, settings); } } moveFileResult.OldFiles.Add(file); _mediaFileService.Delete(file, DeleteMediaFileReason.Upgrade); } if (!isCalibre) { if (copyOnly) { moveFileResult.BookFile = _bookFileMover.CopyBookFile(bookFile, localBook); } else { moveFileResult.BookFile = _bookFileMover.MoveBookFile(bookFile, localBook); } _metadataTagService.WriteTags(bookFile, true); } else { var source = bookFile.Path; moveFileResult.BookFile = _calibre.AddAndConvert(bookFile, settings); if (!copyOnly) { _diskProvider.DeleteFile(source); } } return(moveFileResult); }
public List <ImportResult> Import(List <ImportDecision <LocalTrack> > decisions, bool replaceExisting, DownloadClientItem downloadClientItem = null, ImportMode importMode = ImportMode.Auto) { var qualifiedImports = decisions.Where(c => c.Approved) .GroupBy(c => c.Item.Artist.Id, (i, s) => s .OrderByDescending(c => c.Item.Quality, new QualityModelComparer(s.First().Item.Artist.QualityProfile)) .ThenByDescending(c => c.Item.Size)) .SelectMany(c => c) .ToList(); _logger.Debug($"Importing {qualifiedImports.Count} files. replaceExisting: {replaceExisting}"); var importResults = new List <ImportResult>(); var allImportedTrackFiles = new List <TrackFile>(); var allOldTrackFiles = new List <TrackFile>(); var albumDecisions = decisions.Where(e => e.Item.Album != null && e.Approved) .GroupBy(e => e.Item.Album.Id).ToList(); foreach (var albumDecision in albumDecisions) { var album = albumDecision.First().Item.Album; var newRelease = albumDecision.First().Item.Release; if (replaceExisting) { var artist = albumDecision.First().Item.Artist; var rootFolder = _diskProvider.GetParentFolder(artist.Path); var previousFiles = _mediaFileService.GetFilesByAlbum(album.Id); _logger.Debug($"Deleting {previousFiles.Count} existing files for {album}"); foreach (var previousFile in previousFiles) { var subfolder = rootFolder.GetRelativePath(_diskProvider.GetParentFolder(previousFile.Path)); if (_diskProvider.FileExists(previousFile.Path)) { _logger.Debug("Removing existing track file: {0}", previousFile); _recycleBinProvider.DeleteFile(previousFile.Path, subfolder); } _mediaFileService.Delete(previousFile, DeleteMediaFileReason.Upgrade); } } // set the correct release to be monitored before importing the new files _logger.Debug("Updating release to {0} [{1} tracks]", newRelease, newRelease.TrackCount); album.AlbumReleases = _releaseService.SetMonitored(newRelease); // Publish album edited event. // Deliberatly don't put in the old album since we don't want to trigger an ArtistScan. _eventAggregator.PublishEvent(new AlbumEditedEvent(album, album)); } var filesToAdd = new List <TrackFile>(qualifiedImports.Count); var albumReleasesDict = new Dictionary <int, List <AlbumRelease> >(albumDecisions.Count); var trackImportedEvents = new List <TrackImportedEvent>(qualifiedImports.Count); foreach (var importDecision in qualifiedImports.OrderBy(e => e.Item.Tracks.Select(track => track.AbsoluteTrackNumber).MinOrDefault()) .ThenByDescending(e => e.Item.Size)) { var localTrack = importDecision.Item; var oldFiles = new List <TrackFile>(); try { //check if already imported if (importResults.SelectMany(r => r.ImportDecision.Item.Tracks) .Select(e => e.Id) .Intersect(localTrack.Tracks.Select(e => e.Id)) .Any()) { importResults.Add(new ImportResult(importDecision, "Track has already been imported")); continue; } // cache album releases and set artist to speed up firing the TrackImported events // (otherwise they'll be retrieved from the DB for each track) if (!albumReleasesDict.ContainsKey(localTrack.Album.Id)) { albumReleasesDict.Add(localTrack.Album.Id, localTrack.Album.AlbumReleases.Value); } if (!localTrack.Album.AlbumReleases.IsLoaded) { localTrack.Album.AlbumReleases = albumReleasesDict[localTrack.Album.Id]; } localTrack.Album.Artist = localTrack.Artist; foreach (var track in localTrack.Tracks) { track.Artist = localTrack.Artist; track.AlbumRelease = localTrack.Release; track.Album = localTrack.Album; } var trackFile = new TrackFile { Path = localTrack.Path.CleanFilePath(), Size = localTrack.Size, Modified = localTrack.Modified, DateAdded = DateTime.UtcNow, ReleaseGroup = localTrack.ReleaseGroup, Quality = localTrack.Quality, MediaInfo = localTrack.FileTrackInfo.MediaInfo, AlbumId = localTrack.Album.Id, Artist = localTrack.Artist, Album = localTrack.Album, Tracks = localTrack.Tracks }; bool copyOnly; switch (importMode) { default: case ImportMode.Auto: copyOnly = downloadClientItem != null && !downloadClientItem.CanMoveFiles; break; case ImportMode.Move: copyOnly = false; break; case ImportMode.Copy: copyOnly = true; break; } if (!localTrack.ExistingFile) { trackFile.SceneName = GetSceneReleaseName(downloadClientItem, localTrack); var moveResult = _trackFileUpgrader.UpgradeTrackFile(trackFile, localTrack, copyOnly); oldFiles = moveResult.OldFiles; } else { // Delete existing files from the DB mapped to this path var previousFile = _mediaFileService.GetFileWithPath(trackFile.Path); if (previousFile != null) { _mediaFileService.Delete(previousFile, DeleteMediaFileReason.ManualOverride); } _audioTagService.WriteTags(trackFile, false); } filesToAdd.Add(trackFile); importResults.Add(new ImportResult(importDecision)); if (!localTrack.ExistingFile) { _extraService.ImportTrack(localTrack, trackFile, copyOnly); } allImportedTrackFiles.Add(trackFile); allOldTrackFiles.AddRange(oldFiles); // create all the import events here, but we can't publish until the trackfiles have been // inserted and ids created trackImportedEvents.Add(new TrackImportedEvent(localTrack, trackFile, oldFiles, !localTrack.ExistingFile, downloadClientItem)); } catch (RootFolderNotFoundException e) { _logger.Warn(e, "Couldn't import track " + localTrack); _eventAggregator.PublishEvent(new TrackImportFailedEvent(e, localTrack, !localTrack.ExistingFile, downloadClientItem)); importResults.Add(new ImportResult(importDecision, "Failed to import track, Root folder missing.")); } catch (DestinationAlreadyExistsException e) { _logger.Warn(e, "Couldn't import track " + localTrack); importResults.Add(new ImportResult(importDecision, "Failed to import track, Destination already exists.")); } catch (UnauthorizedAccessException e) { _logger.Warn(e, "Couldn't import track " + localTrack); _eventAggregator.PublishEvent(new TrackImportFailedEvent(e, localTrack, !localTrack.ExistingFile, downloadClientItem)); importResults.Add(new ImportResult(importDecision, "Failed to import track, Permissions error")); } catch (Exception e) { _logger.Warn(e, "Couldn't import track " + localTrack); importResults.Add(new ImportResult(importDecision, "Failed to import track")); } } var watch = new System.Diagnostics.Stopwatch(); watch.Start(); _mediaFileService.AddMany(filesToAdd); _logger.Debug($"Inserted new trackfiles in {watch.ElapsedMilliseconds}ms"); filesToAdd.ForEach(f => f.Tracks.Value.ForEach(t => t.TrackFileId = f.Id)); _trackService.SetFileIds(filesToAdd.SelectMany(x => x.Tracks.Value).ToList()); _logger.Debug($"TrackFileIds updated, total {watch.ElapsedMilliseconds}ms"); // now that trackfiles have been inserted and ids generated, publish the import events foreach (var trackImportedEvent in trackImportedEvents) { _eventAggregator.PublishEvent(trackImportedEvent); } var albumImports = importResults.Where(e => e.ImportDecision.Item.Album != null) .GroupBy(e => e.ImportDecision.Item.Album.Id).ToList(); foreach (var albumImport in albumImports) { var release = albumImport.First().ImportDecision.Item.Release; var album = albumImport.First().ImportDecision.Item.Album; var artist = albumImport.First().ImportDecision.Item.Artist; if (albumImport.Where(e => e.Errors.Count == 0).ToList().Count > 0 && artist != null && album != null) { _eventAggregator.PublishEvent(new AlbumImportedEvent( artist, album, release, allImportedTrackFiles.Where(s => s.AlbumId == album.Id).ToList(), allOldTrackFiles.Where(s => s.AlbumId == album.Id).ToList(), replaceExisting, downloadClientItem)); } } //Adding all the rejected decisions importResults.AddRange(decisions.Where(c => !c.Approved) .Select(d => new ImportResult(d, d.Rejections.Select(r => r.Reason).ToArray()))); return(importResults); }
public void ImportExtraFiles(LocalBook localBook, BookFile bookFile, bool isReadOnly) { if (!_configService.ImportExtraFiles) { return; } var sourcePath = localBook.Path; var sourceFolder = _diskProvider.GetParentFolder(sourcePath); var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath); var files = _diskProvider.GetFiles(sourceFolder, SearchOption.TopDirectoryOnly); var wantedExtensions = _configService.ExtraFileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(e => e.Trim(' ', '.')) .ToList(); var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase)).ToList(); var filteredFilenames = new List <string>(); var hasNfo = false; foreach (var matchingFilename in matchingFilenames) { // Filter out duplicate NFO files if (matchingFilename.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase)) { if (hasNfo) { continue; } hasNfo = true; } filteredFilenames.Add(matchingFilename); } foreach (var matchingFilename in filteredFilenames) { var matchingExtension = wantedExtensions.FirstOrDefault(e => matchingFilename.EndsWith(e)); if (matchingExtension == null) { continue; } try { foreach (var extraFileManager in _extraFileManagers) { var extension = Path.GetExtension(matchingFilename); var extraFile = extraFileManager.Import(localBook.Author, bookFile, matchingFilename, extension, isReadOnly); if (extraFile != null) { break; } } } catch (Exception ex) { _logger.Warn(ex, "Failed to import extra file: {0}", matchingFilename); } } }
public void Scan(Movie movie) { //Try renaming the movie path in case anything changed such as year, title or something else. _renameMovieFiles.RenameMoviePath(movie, true); var rootFolder = _diskProvider.GetParentFolder(movie.Path); if (!_diskProvider.FolderExists(rootFolder)) { _logger.Warn("Movies' root folder ({0}) doesn't exist.", rootFolder); _eventAggregator.PublishEvent(new MovieScanSkippedEvent(movie, MovieScanSkippedReason.RootFolderDoesNotExist)); return; } if (_diskProvider.GetDirectories(rootFolder).Empty()) { _logger.Warn("Movies' root folder ({0}) is empty.", rootFolder); _eventAggregator.PublishEvent(new MovieScanSkippedEvent(movie, MovieScanSkippedReason.RootFolderIsEmpty)); return; } _logger.ProgressInfo("Scanning disk for {0}", movie.Title); if (!_diskProvider.FolderExists(movie.Path)) { if (movie.MovieFileId != 0) { //Since there is no folder, there can't be any files right? _mediaFileTableCleanupService.Clean(movie, new List <string>()); _logger.Debug("Movies folder doesn't exist: {0}", movie.Path); } else if (_configService.CreateEmptySeriesFolders && _diskProvider.FolderExists(rootFolder)) { _logger.Debug("Creating missing movies folder: {0}", movie.Path); _diskProvider.CreateFolder(movie.Path); SetPermissions(movie.Path); } else { _logger.Debug("Movies folder doesn't exist: {0}", movie.Path); } _eventAggregator.PublishEvent(new MovieScanSkippedEvent(movie, MovieScanSkippedReason.MovieFolderDoesNotExist)); return; } var videoFilesStopwatch = Stopwatch.StartNew(); var mediaFileList = FilterFiles(movie, GetVideoFiles(movie.Path)).ToList(); videoFilesStopwatch.Stop(); _logger.Trace("Finished getting movie files for: {0} [{1}]", movie, videoFilesStopwatch.Elapsed); _logger.Debug("{0} Cleaning up media files in DB", movie); _mediaFileTableCleanupService.Clean(movie, mediaFileList); var decisionsStopwatch = Stopwatch.StartNew(); var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, movie, true); decisionsStopwatch.Stop(); _logger.Trace("Import decisions complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed); _importApprovedMovies.Import(decisions, false); _logger.Info("Completed scanning disk for {0}", movie.Title); _eventAggregator.PublishEvent(new MovieScannedEvent(movie)); }