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); CompletedScanning(series); }
private List <ImportResult> ProcessFolder(DirectoryInfo directoryInfo, Series series, DownloadClientItem downloadClientItem = null) { if (_seriesService.SeriesPathExists(directoryInfo.FullName)) { _logger.Warn("Unable to process folder that is mapped to an existing show"); return(new List <ImportResult>()); } var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); var quality = QualityParser.ParseQuality(cleanedUpName); _logger.Debug("{0} folder quality: {1}", cleanedUpName, quality); var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName); if (downloadClientItem == null) { foreach (var videoFile in videoFiles) { if (_diskProvider.IsFileLocked(videoFile)) { return(new List <ImportResult> { FileIsLockedResult(videoFile) }); } } } var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), series, true, quality); var importResults = _importApprovedEpisodes.Import(decisions, true, downloadClientItem); if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) && importResults.Any() && ShouldDeleteFolder(directoryInfo, series)) { _logger.Debug("Deleting folder after importing valid files"); _diskProvider.DeleteFolder(directoryInfo.FullName, true); } return(importResults); }
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)); }
private List <ManualImportItem> ProcessFolder(string rootFolder, string baseFolder, string downloadId, int?seriesId, bool filterExistingFiles) { DownloadClientItem downloadClientItem = null; var directoryInfo = new DirectoryInfo(baseFolder); var series = seriesId.HasValue ? _seriesService.GetSeries(seriesId.Value) : _parsingService.GetSeries(directoryInfo.Name); if (downloadId.IsNotNullOrWhiteSpace()) { var trackedDownload = _trackedDownloadService.Find(downloadId); downloadClientItem = trackedDownload.DownloadItem; if (series == null) { series = trackedDownload.RemoteEpisode?.Series; } } if (series == null) { var files = _diskScanService.FilterFiles(baseFolder, _diskScanService.GetVideoFiles(baseFolder, false)); var subfolders = _diskScanService.FilterFiles(baseFolder, _diskProvider.GetDirectories(baseFolder)); var processedFiles = files.Select(file => ProcessFile(rootFolder, baseFolder, file, downloadId)); var processedFolders = subfolders.SelectMany(subfolder => ProcessFolder(rootFolder, subfolder, downloadId, null, filterExistingFiles)); return(processedFiles.Concat(processedFolders).Where(i => i != null).ToList()); } var folderInfo = Parser.Parser.ParseTitle(directoryInfo.Name); var seriesFiles = _diskScanService.GetVideoFiles(baseFolder).ToList(); var decisions = _importDecisionMaker.GetImportDecisions(seriesFiles, series, downloadClientItem, folderInfo, SceneSource(series, baseFolder), filterExistingFiles); return(decisions.Select(decision => MapItem(decision, rootFolder, downloadId, directoryInfo.Name)).ToList()); }
private List <ManualImportItem> ProcessFolder(string folder, string downloadId) { var directoryInfo = new DirectoryInfo(folder); var series = _parsingService.GetSeries(directoryInfo.Name); if (series == null && downloadId.IsNotNullOrWhiteSpace()) { var trackedDownload = _trackedDownloadService.Find(downloadId); series = trackedDownload.RemoteEpisode.Series; } if (series == null) { var files = _diskScanService.GetVideoFiles(folder); return(files.Select(file => ProcessFile(file, downloadId, folder)).Where(i => i != null).ToList()); } var folderInfo = Parser.Parser.ParseTitle(directoryInfo.Name); var seriesFiles = _diskScanService.GetVideoFiles(folder).ToList(); var decisions = _importDecisionMaker.GetImportDecisions(seriesFiles, series, folderInfo, SceneSource(series, folder)); return(decisions.Select(decision => MapItem(decision, folder, downloadId)).ToList()); }
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); }
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 List <ImportDecision> ProcessFiles(Series series, params string[] videoFiles) { var decisions = _importDecisionMaker.GetImportDecisions(videoFiles, series, true); return(_importApprovedEpisodes.Import(decisions, true)); }
private ManualImportItem ProcessFile(string rootFolder, string baseFolder, string file, string downloadId, Movie movie = null) { DownloadClientItem downloadClientItem = null; var relativeFile = baseFolder.GetRelativePath(file); if (movie == null) { _parsingService.GetMovie(relativeFile.Split('\\', '/')[0]); } if (movie == null) { movie = _parsingService.GetMovie(relativeFile); } if (downloadId.IsNotNullOrWhiteSpace()) { var trackedDownload = _trackedDownloadService.Find(downloadId); downloadClientItem = trackedDownload?.DownloadItem; if (movie == null) { movie = trackedDownload?.RemoteMovie?.Movie; } } if (movie == null) { var relativeParseInfo = Parser.Parser.ParseMoviePath(relativeFile); if (relativeParseInfo != null) { movie = _movieService.FindByTitle(relativeParseInfo.MovieTitle, relativeParseInfo.Year); } } if (movie == null) { var localMovie = new LocalMovie(); localMovie.Path = file; localMovie.Quality = QualityParser.ParseQuality(file); localMovie.Languages = LanguageParser.ParseLanguages(file); localMovie.Size = _diskProvider.GetFileSize(file); return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), rootFolder, downloadId, null); } var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file }, movie, downloadClientItem, null, SceneSource(movie, baseFolder)); if (importDecisions.Any()) { return MapItem(importDecisions.First(), rootFolder, downloadId, null); } return new ManualImportItem { DownloadId = downloadId, Path = file, RelativePath = rootFolder.GetRelativePath(file), Name = Path.GetFileNameWithoutExtension(file), Rejections = new List<Rejection>() }; }
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); }
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)); }
private Response Search() { if (Request.Query.Id == 0) { //Todo error handling } RootFolder rootFolder = _rootFolderService.Get(Request.Query.Id); int page = Request.Query.page; int per_page = Request.Query.per_page; int min = (page - 1) * per_page; int max = page * per_page; var unmapped = rootFolder.UnmappedFolders.OrderBy(f => f.Name).ToList(); int total_count = unmapped.Count; if (Request.Query.total_entries.HasValue) { total_count = Request.Query.total_entries; } max = total_count >= max ? max : total_count; var paged = unmapped.GetRange(min, max - min); var mapped = paged.Select(f => { Core.Movies.Movie m = null; var mappedMovie = _mappedMovies.Find(f.Name); if (mappedMovie != null) { return(mappedMovie); } var parsedTitle = _parsingService.ParseMinimalPathMovieInfo(f.Name); parsedTitle.ImdbId = Parser.ParseImdbId(parsedTitle.SimpleReleaseTitle); if (parsedTitle == null) { m = new Core.Movies.Movie { Title = f.Name.Replace(".", " ").Replace("-", " "), Path = f.Path, }; } else { m = new Core.Movies.Movie { Title = parsedTitle.MovieTitle, Year = parsedTitle.Year, ImdbId = parsedTitle.ImdbId, Path = f.Path }; } var files = _diskScanService.GetVideoFiles(f.Path); var decisions = _importDecisionMaker.GetImportDecisions(files.ToList(), m, true); var decision = decisions.Where(d => d.Approved && !d.Rejections.Any()).FirstOrDefault(); if (decision != null) { var local = decision.LocalMovie; m.MovieFile = new MovieFile { Path = local.Path, Edition = local.ParsedMovieInfo.Edition, Quality = local.Quality, MediaInfo = local.MediaInfo, ReleaseGroup = local.ParsedMovieInfo.ReleaseGroup, RelativePath = f.Path.GetRelativePath(local.Path) }; } mappedMovie = _searchProxy.MapMovieToTmdbMovie(m); if (mappedMovie != null) { mappedMovie.Monitored = true; _mappedMovies.Set(f.Name, mappedMovie, TimeSpan.FromDays(2)); return(mappedMovie); } return(null); }); return(new PagingResource <MovieResource> { Page = page, PageSize = per_page, SortDirection = SortDirection.Ascending, SortKey = Request.Query.sort_by, TotalRecords = total_count - mapped.Where(m => m == null).Count(), Records = MapToResource(mapped.Where(m => m != null)).ToList() }.AsResponse()); }
private List <ImportResult> ProcessFolder(IDirectoryInfo directoryInfo, ImportMode importMode, Artist artist, DownloadClientItem downloadClientItem) { if (_artistService.ArtistPathExists(directoryInfo.FullName)) { _logger.Warn("Unable to process folder that is mapped to an existing artist"); return(new List <ImportResult>()); } var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); var folderInfo = Parser.Parser.ParseAlbumTitle(directoryInfo.Name); var trackInfo = new ParsedTrackInfo { }; if (folderInfo != null) { _logger.Debug("{0} folder quality: {1}", cleanedUpName, folderInfo.Quality); trackInfo = new ParsedTrackInfo { AlbumTitle = folderInfo.AlbumTitle, ArtistTitle = folderInfo.ArtistName, Quality = folderInfo.Quality, ReleaseGroup = folderInfo.ReleaseGroup, ReleaseHash = folderInfo.ReleaseHash, }; } else { trackInfo = null; } var audioFiles = _diskScanService.FilterFiles(directoryInfo.FullName, _diskScanService.GetAudioFiles(directoryInfo.FullName)); if (downloadClientItem == null) { foreach (var audioFile in audioFiles) { if (_diskProvider.IsFileLocked(audioFile.FullName)) { return(new List <ImportResult> { FileIsLockedResult(audioFile.FullName) }); } } } var decisions = _importDecisionMaker.GetImportDecisions(audioFiles, artist, trackInfo); var importResults = _importApprovedTracks.Import(decisions, true, downloadClientItem, importMode); if (importMode == ImportMode.Auto) { importMode = (downloadClientItem == null || downloadClientItem.CanMoveFiles) ? ImportMode.Move : ImportMode.Copy; } if (importMode == ImportMode.Move && importResults.Any(i => i.Result == ImportResultType.Imported) && ShouldDeleteFolder(directoryInfo, artist)) { _logger.Debug("Deleting folder after importing valid files"); _diskProvider.DeleteFolder(directoryInfo.FullName, true); } return(importResults); }
public void Scan(Movie movie) { var rootFolder = _rootFolderService.GetBestRootFolderPath(movie.Path); var movieFolderExists = _diskProvider.FolderExists(movie.Path); if (!movieFolderExists) { if (!_diskProvider.FolderExists(rootFolder)) { _logger.Warn("Movie's root folder ({0}) doesn't exist.", rootFolder); _eventAggregator.PublishEvent(new MovieScanSkippedEvent(movie, MovieScanSkippedReason.RootFolderDoesNotExist)); return; } if (_diskProvider.FolderEmpty(rootFolder)) { _logger.Warn("Movie's root folder ({0}) is empty.", rootFolder); _eventAggregator.PublishEvent(new MovieScanSkippedEvent(movie, MovieScanSkippedReason.RootFolderIsEmpty)); return; } } _logger.ProgressInfo("Scanning disk for {0}", movie.Title); if (!movieFolderExists) { if (_configService.CreateEmptyMovieFolders) { if (_configService.DeleteEmptyFolders) { _logger.Debug("Not creating missing movie folder: {0} because delete empty series folders is enabled", movie.Path); } else { _logger.Debug("Creating missing series folder: {0}", movie.Path); _diskProvider.CreateFolder(movie.Path); SetPermissions(movie.Path); } } else { _logger.Debug("Movies folder doesn't exist: {0}", movie.Path); } CleanMediaFiles(movie, new List <string>()); CompletedScanning(movie); return; } var videoFilesStopwatch = Stopwatch.StartNew(); var mediaFileList = FilterPaths(movie.Path, GetVideoFiles(movie.Path)).ToList(); videoFilesStopwatch.Stop(); _logger.Trace("Finished getting movie files for: {0} [{1}]", movie, videoFilesStopwatch.Elapsed); CleanMediaFiles(movie, mediaFileList); var decisionsStopwatch = Stopwatch.StartNew(); var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, movie); decisionsStopwatch.Stop(); _logger.Trace("Import decisions complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed); _importApprovedMovies.Import(decisions, false); RemoveEmptyMovieFolder(movie.Path); CompletedScanning(movie); }
public void Scan(Movie movie) { var rootFolder = _rootFolderService.GetBestRootFolderPath(movie.Path); var movieFolderExists = _diskProvider.FolderExists(movie.Path); if (!movieFolderExists) { if (!_diskProvider.FolderExists(rootFolder)) { _logger.Warn("Movie's root folder ({0}) doesn't exist.", rootFolder); _eventAggregator.PublishEvent(new MovieScanSkippedEvent(movie, MovieScanSkippedReason.RootFolderDoesNotExist)); return; } if (_diskProvider.FolderEmpty(rootFolder)) { _logger.Warn("Movie's root folder ({0}) is empty.", rootFolder); _eventAggregator.PublishEvent(new MovieScanSkippedEvent(movie, MovieScanSkippedReason.RootFolderIsEmpty)); return; } } _logger.ProgressInfo("Scanning disk for {0}", movie.Title); if (!movieFolderExists) { if (_configService.CreateEmptyMovieFolders) { if (_configService.DeleteEmptyFolders) { _logger.Debug("Not creating missing movie folder: {0} because delete empty movie folders is enabled", movie.Path); } else { _logger.Debug("Creating missing movie folder: {0}", movie.Path); _diskProvider.CreateFolder(movie.Path); SetPermissions(movie.Path); } } else { _logger.Debug("Movie's folder doesn't exist: {0}", movie.Path); } CleanMediaFiles(movie, new List <string>()); CompletedScanning(movie); return; } var videoFilesStopwatch = Stopwatch.StartNew(); var mediaFileList = FilterPaths(movie.Path, GetVideoFiles(movie.Path)).ToList(); videoFilesStopwatch.Stop(); _logger.Trace("Finished getting movie files for: {0} [{1}]", movie, videoFilesStopwatch.Elapsed); CleanMediaFiles(movie, mediaFileList); var movieFiles = _mediaFileService.GetFilesByMovie(movie.Id); var unmappedFiles = MediaFileService.FilterExistingFiles(mediaFileList, movieFiles, movie); var decisionsStopwatch = Stopwatch.StartNew(); var decisions = _importDecisionMaker.GetImportDecisions(unmappedFiles, movie, false); decisionsStopwatch.Stop(); _logger.Trace("Import decisions complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed); _importApprovedMovies.Import(decisions, false); // Update existing files that have a different file size var fileInfoStopwatch = Stopwatch.StartNew(); var filesToUpdate = new List <MovieFile>(); foreach (var file in movieFiles) { var path = Path.Combine(movie.Path, file.RelativePath); var fileSize = _diskProvider.GetFileSize(path); if (file.Size == fileSize) { continue; } file.Size = fileSize; if (!_updateMediaInfoService.Update(file, movie)) { filesToUpdate.Add(file); } } // Update any files that had a file size change, but didn't get media info updated. if (filesToUpdate.Any()) { _mediaFileService.Update(filesToUpdate); } fileInfoStopwatch.Stop(); _logger.Trace("Reprocessing existing files complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed); RemoveEmptyMovieFolder(movie.Path); CompletedScanning(movie); }
private List <ManualImportItem> ProcessFolder(string rootFolder, string baseFolder, string downloadId, int?seriesId, bool filterExistingFiles) { DownloadClientItem downloadClientItem = null; Series series = null; var directoryInfo = new DirectoryInfo(baseFolder); if (seriesId.HasValue) { series = _seriesService.GetSeries(seriesId.Value); } else { try { series = _parsingService.GetSeries(directoryInfo.Name); } catch (MultipleSeriesFoundException e) { _logger.Warn(e, "Unable to find series from title"); } } if (downloadId.IsNotNullOrWhiteSpace()) { var trackedDownload = _trackedDownloadService.Find(downloadId); downloadClientItem = trackedDownload.DownloadItem; if (series == null) { series = trackedDownload.RemoteEpisode?.Series; } } if (series == null) { // Filter paths based on the rootFolder, so files in subfolders that should be ignored are ignored. // It will lead to some extra directories being checked for files, but it saves the processing of them and is cleaner than // teaching FilterPaths to know whether it's processing a file or a folder and changing it's filtering based on that. // If the series is unknown for the directory and there are more than 100 files in the folder don't process the items before returning. var files = _diskScanService.FilterPaths(rootFolder, _diskScanService.GetVideoFiles(baseFolder, false)); if (files.Count() > 100) { return(ProcessDownloadDirectory(rootFolder, files)); } var subfolders = _diskScanService.FilterPaths(rootFolder, _diskProvider.GetDirectories(baseFolder)); var processedFiles = files.Select(file => ProcessFile(rootFolder, baseFolder, file, downloadId)); var processedFolders = subfolders.SelectMany(subfolder => ProcessFolder(rootFolder, subfolder, downloadId, null, filterExistingFiles)); return(processedFiles.Concat(processedFolders).Where(i => i != null).ToList()); } var folderInfo = Parser.Parser.ParseTitle(directoryInfo.Name); var seriesFiles = _diskScanService.FilterPaths(rootFolder, _diskScanService.GetVideoFiles(baseFolder).ToList()); var decisions = _importDecisionMaker.GetImportDecisions(seriesFiles, series, downloadClientItem, folderInfo, SceneSource(series, baseFolder), filterExistingFiles); return(decisions.Select(decision => MapItem(decision, rootFolder, downloadId, directoryInfo.Name)).ToList()); }
private List <ImportResult> ProcessFolder(IDirectoryInfo directoryInfo, ImportMode importMode, Author author, DownloadClientItem downloadClientItem) { if (_authorService.AuthorPathExists(directoryInfo.FullName)) { _logger.Warn("Unable to process folder that is mapped to an existing author"); return(new List <ImportResult>()); } var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); var folderInfo = Parser.Parser.ParseBookTitle(directoryInfo.Name); var trackInfo = new ParsedTrackInfo { }; if (folderInfo != null) { _logger.Debug("{0} folder quality: {1}", cleanedUpName, folderInfo.Quality); trackInfo = new ParsedTrackInfo { AlbumTitle = folderInfo.BookTitle, ArtistTitle = folderInfo.AuthorName, Quality = folderInfo.Quality, ReleaseGroup = folderInfo.ReleaseGroup, ReleaseHash = folderInfo.ReleaseHash, }; } else { trackInfo = null; } var audioFiles = _diskScanService.FilterFiles(directoryInfo.FullName, _diskScanService.GetBookFiles(directoryInfo.FullName)); if (downloadClientItem == null) { foreach (var audioFile in audioFiles) { if (_diskProvider.IsFileLocked(audioFile.FullName)) { return(new List <ImportResult> { FileIsLockedResult(audioFile.FullName) }); } } } var idOverrides = new IdentificationOverrides { Author = author }; var idInfo = new ImportDecisionMakerInfo { DownloadClientItem = downloadClientItem, ParsedTrackInfo = trackInfo }; var idConfig = new ImportDecisionMakerConfig { Filter = FilterFilesType.None, NewDownload = true, SingleRelease = false, IncludeExisting = false, AddNewAuthors = false }; var decisions = _importDecisionMaker.GetImportDecisions(audioFiles, idOverrides, idInfo, idConfig); var importResults = _importApprovedTracks.Import(decisions, true, downloadClientItem, importMode); if (importMode == ImportMode.Auto) { importMode = (downloadClientItem == null || downloadClientItem.CanMoveFiles) ? ImportMode.Move : ImportMode.Copy; } if (importMode == ImportMode.Move && importResults.Any(i => i.Result == ImportResultType.Imported) && ShouldDeleteFolder(directoryInfo, author)) { _logger.Debug("Deleting folder after importing valid files"); _diskProvider.DeleteFolder(directoryInfo.FullName, true); } return(importResults); }
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); }