예제 #1
0
        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);
        }
예제 #3
0
        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));
        }
예제 #4
0
        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());
        }
예제 #5
0
        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());
        }
예제 #6
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);
        }
예제 #7
0
        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));
        }
예제 #9
0
        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>()
            };
        }
예제 #10
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);
        }
예제 #11
0
        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));
        }
예제 #12
0
        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());
        }
예제 #13
0
        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);
        }
예제 #14
0
        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);
        }
예제 #15
0
        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);
        }
예제 #16
0
        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);
        }
예제 #18
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);
        }