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); }
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()))); }
private void LinkRootFolderPath(params AuthorResource[] authors) { var rootFolders = _rootFolderService.All(); foreach (var author in authors) { author.RootFolderPath = _rootFolderService.GetBestRootFolderPath(author.Path, rootFolders); } }
protected override bool IsValid(PropertyValidatorContext context) { if (context.PropertyValue == null) { return(true); } return(!_rootFolderService.All().Any(s => context.PropertyValue.ToString().IsParentPath(s.Path))); }
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())); } }
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())); }