Beispiel #1
0
        public List <TagDetails> Details()
        {
            var tags          = All();
            var delayProfiles = _delayProfileService.All();
            var importLists   = _importListFactory.All();
            var notifications = _notificationFactory.All();
            var restrictions  = _releaseProfileService.All();
            var artists       = _artistService.GetAllArtists();
            var rootFolders   = _rootFolderService.All();

            var details = new List <TagDetails>();

            foreach (var tag in tags)
            {
                details.Add(new TagDetails
                {
                    Id              = tag.Id,
                    Label           = tag.Label,
                    DelayProfileIds = delayProfiles.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
                    ImportListIds   = importLists.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
                    NotificationIds = notifications.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
                    RestrictionIds  = restrictions.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
                    ArtistIds       = artists.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
                    RootFolderIds   = rootFolders.Where(c => c.DefaultTags.Contains(tag.Id)).Select(c => c.Id).ToList()
                });
            }

            return(details);
        }
Beispiel #2
0
        public override HealthCheck Check()
        {
            var clients     = _downloadClientProvider.GetDownloadClients();
            var rootFolders = _rootFolderService.All();

            foreach (var client in clients)
            {
                try
                {
                    var status  = client.GetStatus();
                    var folders = status.OutputRootFolders;
                    foreach (var folder in folders)
                    {
                        if (rootFolders.Any(r => r.Path.PathEquals(folder.FullPath)))
                        {
                            return(new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("DownloadClientCheckDownloadingToRoot"), client.Definition.Name, folder.FullPath), "#downloads-in-root-folder"));
                        }
                    }
                }
                catch (DownloadClientException ex)
                {
                    _logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, "Unknown error occured in DownloadClientRootFolderCheck HealthCheck");
                }
            }

            return(new HealthCheck(GetType()));
        }
        private bool CalibreLibraryOnlyUsedOnce(RootFolderResource settings)
        {
            var newUri = GetLibraryUri(settings);

            return(!_rootFolderService.All().Exists(x => x.Id != settings.Id &&
                                                    x.CalibreSettings != null &&
                                                    GetLibraryUri(x.CalibreSettings) == newUri));
        }
        protected override bool IsValid(PropertyValidatorContext context)
        {
            if (context.PropertyValue == null)
            {
                return(true);
            }

            return(!_rootFolderService.All().Exists(r => r.Path.PathEquals(context.PropertyValue.ToString())));
        }
Beispiel #5
0
        private void LinkRootFolderPath(params AuthorResource[] authors)
        {
            var rootFolders = _rootFolderService.All();

            foreach (var author in authors)
            {
                author.RootFolderPath = _rootFolderService.GetBestRootFolderPath(author.Path, rootFolders);
            }
        }
Beispiel #6
0
        protected override bool IsValid(PropertyValidatorContext context)
        {
            if (context.PropertyValue == null)
            {
                return(true);
            }

            return(!_rootFolderService.All().Any(s => context.PropertyValue.ToString().IsParentPath(s.Path)));
        }
Beispiel #7
0
        public List <DiskSpace> GetFreeSpace()
        {
            var importantRootFolders = _rootFolderService.All().Select(x => x.Path).ToList();

            var optionalRootFolders = GetFixedDisksRootPaths().Except(importantRootFolders).Distinct().ToList();

            var diskSpace = GetDiskSpace(importantRootFolders).Concat(GetDiskSpace(optionalRootFolders, true)).ToList();

            return(diskSpace);
        }
        public void Delete(int id)
        {
            if (_artistService.GetAllArtists().Any(c => c.QualityProfileId == id) ||
                _importListFactory.All().Any(c => c.ProfileId == id) ||
                _rootFolderService.All().Any(c => c.DefaultQualityProfileId == id))
            {
                var profile = _profileRepository.Get(id);
                throw new QualityProfileInUseException(profile.Name);
            }

            _profileRepository.Delete(id);
        }
        public void Delete(int id)
        {
            var profile = _profileRepository.Get(id);

            if (profile.Name == NONE_PROFILE_NAME ||
                _authorService.GetAllAuthors().Any(c => c.MetadataProfileId == id) ||
                _importListFactory.All().Any(c => c.MetadataProfileId == id) ||
                _rootFolderService.All().Any(c => c.DefaultMetadataProfileId == id))
            {
                throw new MetadataProfileInUseException(profile.Name);
            }

            _profileRepository.Delete(id);
        }
        private void Rescan(List <Artist> artists, bool isNew, CommandTrigger trigger, bool infoUpdated)
        {
            var rescanAfterRefresh = _configService.RescanAfterRefresh;
            var shouldRescan       = true;
            var filter             = FilterFilesType.Matched;
            var folders            = _rootFolderService.All().Select(x => x.Path).ToList();

            if (isNew)
            {
                _logger.Trace("Forcing rescan. Reason: New artist added");
                shouldRescan = true;

                // only rescan artist folders - otherwise it can be super slow for
                // badly organized / partly matched libraries
                folders = artists.Select(x => x.Path).ToList();
            }
            else if (rescanAfterRefresh == RescanAfterRefreshType.Never)
            {
                _logger.Trace("Skipping rescan. Reason: never rescan after refresh");
                shouldRescan = false;
            }
            else if (rescanAfterRefresh == RescanAfterRefreshType.AfterManual && trigger != CommandTrigger.Manual)
            {
                _logger.Trace("Skipping rescan. Reason: not after automatic refreshes");
                shouldRescan = false;
            }
            else if (!infoUpdated && trigger != CommandTrigger.Manual)
            {
                _logger.Trace("Skipping rescan. Reason: no metadata updated after automatic refresh");
                shouldRescan = false;
            }
            else if (!infoUpdated)
            {
                _logger.Trace("No metadata updated, only scanning new files");
                filter = FilterFilesType.Known;
            }

            if (shouldRescan)
            {
                // some metadata has updated so rescan unmatched
                // (but don't add new artists to reduce repeated searches against api)
                _commandQueueManager.Push(new RescanFoldersCommand(folders, filter, false, artists.Select(x => x.Id).ToList()));
            }
        }
Beispiel #11
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);
        }
        public override HealthCheck Check()
        {
            var rootFolders = _rootFolderService.All().Where(x => x.IsCalibreLibrary);

            foreach (var folder in rootFolders)
            {
                try
                {
                    var calibreIsLocal = folder.CalibreSettings.Host == "127.0.0.1" || folder.CalibreSettings.Host == "localhost";

                    var files = _calibreProxy.GetAllBookFilePaths(folder.CalibreSettings);
                    if (files.Any())
                    {
                        var file = files.First();

                        // This directory structure is forced by calibre
                        var bookFolder    = Path.GetDirectoryName(file);
                        var authorFolder  = Path.GetDirectoryName(bookFolder);
                        var libraryFolder = Path.GetDirectoryName(authorFolder);

                        var osPath = new OsPath(libraryFolder);

                        if (!osPath.IsValid)
                        {
                            if (!calibreIsLocal)
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote calibre for root folder {folder.Name} reports files in {libraryFolder} but this is not a valid {_osInfo.Name} path.  Review your remote path mappings and root folder settings.", "#bad_remote_path_mapping"));
                            }
                            else if (_osInfo.IsDocker)
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; calibre for root folder {folder.Name} reports files in {libraryFolder} but this is not a valid {_osInfo.Name} path.  Review your remote path mappings and download client settings.", "#docker_bad_remote_path_mapping"));
                            }
                            else
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Local calibre server for root folder {folder.Name} reports files in {libraryFolder} but this is not a valid {_osInfo.Name} path.  Review your download client settings.", "#bad_download_client_settings"));
                            }
                        }

                        if (!_diskProvider.FolderExists(libraryFolder))
                        {
                            if (_osInfo.IsDocker)
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; calibre server for root folder {folder.Name} places downloads in {libraryFolder} but this directory does not appear to exist inside the container.  Review your remote path mappings and container volume settings.", "#docker_bad_remote_path_mapping"));
                            }
                            else if (!calibreIsLocal)
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote calibre server for root folder {folder.Name} places downloads in {libraryFolder} but this directory does not appear to exist.  Likely missing or incorrect remote path mapping.", "#bad_remote_path_mapping"));
                            }
                            else
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Calibre server for root folder {folder.Name} places downloads in {libraryFolder} but Readarr cannot see this directory.  You may need to adjust the folder's permissions or add a remote path mapping if calibre is running in docker", "#permissions_error"));
                            }
                        }

                        if (!_diskProvider.FileExists(file))
                        {
                            if (_osInfo.IsDocker)
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; calibre server for root folder {folder.Name} listed file {file} but this file does not appear to exist inside the container.  Review permissions for {libraryFolder} and PUID/PGID container settings", "#docker_bad_remote_path_mapping"));
                            }
                            else if (!calibreIsLocal)
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote calibre server for root folder {folder.Name} listed file {file} but this file does not appear to exist.  Review permissions for {libraryFolder}", "#permissions_error"));
                            }
                            else
                            {
                                return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Calibre server for root folder {folder.Name} listed file {file} but Readarr cannot see this file.  Review permissions for {libraryFolder}", "#permissions_error"));
                            }
                        }

                        if (!libraryFolder.PathEquals(folder.Path))
                        {
                            return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Calibre for root folder {folder.Name} reports files in {libraryFolder} but this is not the same as the root folder path {folder.Path} you chose.  You may need to edit any remote path mapping or delete the root folder and re_create with the correct path", "#calibre_root_does_not_match"));
                        }
                    }
                }
                catch (DownloadClientException ex)
                {
                    _logger.Debug(ex, "Unable to communicate with calibre server for root folder {0}", folder.Name);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, "Unknown error occured in CalibreRootFolderCheck HealthCheck");
                }
            }

            return(new HealthCheck(GetType()));
        }