public void Import(TrackedDownload trackedDownload) { trackedDownload.State = TrackedDownloadState.Importing; var outputPath = trackedDownload.DownloadItem.OutputPath.FullPath; var importResults = _downloadedEpisodesImportService.ProcessPath(outputPath, ImportMode.Auto, trackedDownload.RemoteEpisode.Series, trackedDownload.DownloadItem); var allEpisodesImported = importResults.Where(c => c.Result == ImportResultType.Imported) .SelectMany(c => c.ImportDecision.LocalEpisode.Episodes) .Count() >= Math.Max(1, trackedDownload.RemoteEpisode.Episodes.Count); if (allEpisodesImported) { trackedDownload.State = TrackedDownloadState.Imported; _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload)); return; } // Double check if all episodes were imported by checking the history if at least one // file was imported. This will allow the decision engine to reject already imported // episode files and still mark the download complete when all files are imported. if (importResults.Any(c => c.Result == ImportResultType.Imported)) { var historyItems = _historyService.FindByDownloadId(trackedDownload.DownloadItem.DownloadId) .OrderByDescending(h => h.Date) .ToList(); var allEpisodesImportedInHistory = _trackedDownloadAlreadyImported.IsImported(trackedDownload, historyItems); if (allEpisodesImportedInHistory) { trackedDownload.State = TrackedDownloadState.Imported; _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload)); return; } } trackedDownload.State = TrackedDownloadState.ImportPending; if (importResults.Empty()) { trackedDownload.Warn("No files found are eligible for import in {0}", outputPath); } if (importResults.Any(c => c.Result != ImportResultType.Imported)) { var statusMessages = importResults .Where(v => v.Result != ImportResultType.Imported) .Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalEpisode.Path), v.Errors)) .ToArray(); trackedDownload.Warn(statusMessages); } }
public bool VerifyImport(TrackedDownload trackedDownload, List <ImportResult> importResults) { var allEpisodesImported = importResults.Where(c => c.Result == ImportResultType.Imported) .SelectMany(c => c.ImportDecision.LocalEpisode.Episodes) .Count() >= Math.Max(1, trackedDownload.RemoteEpisode.Episodes.Count); if (allEpisodesImported) { _logger.Debug("All episodes were imported for {0}", trackedDownload.DownloadItem.Title); trackedDownload.State = TrackedDownloadState.Imported; _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload, trackedDownload.RemoteEpisode.Series.Id)); return(true); } // Double check if all episodes were imported by checking the history if at least one // file was imported. This will allow the decision engine to reject already imported // episode files and still mark the download complete when all files are imported. // EDGE CASE: This process relies on EpisodeIds being consistent between executions, if a series is updated // and an episode is removed, but later comes back with a different ID then Sonarr will treat it as incomplete. // Since imports should be relatively fast and these types of data changes are infrequent this should be quite // safe, but commenting for future benefit. var atLeastOneEpisodeImported = importResults.Any(c => c.Result == ImportResultType.Imported); var historyItems = _historyService.FindByDownloadId(trackedDownload.DownloadItem.DownloadId) .OrderByDescending(h => h.Date) .ToList(); var allEpisodesImportedInHistory = _trackedDownloadAlreadyImported.IsImported(trackedDownload, historyItems); if (allEpisodesImportedInHistory) { if (atLeastOneEpisodeImported) { _logger.Debug("All episodes were imported in history for {0}", trackedDownload.DownloadItem.Title); trackedDownload.State = TrackedDownloadState.Imported; _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload, trackedDownload.RemoteEpisode.Series.Id)); return(true); } _logger.Debug() .Message("No Episodes were just imported, but all episodes were previously imported, possible issue with download history.") .Property("SeriesId", trackedDownload.RemoteEpisode.Series.Id) .Property("DownloadId", trackedDownload.DownloadItem.DownloadId) .Property("Title", trackedDownload.DownloadItem.Title) .Property("Path", trackedDownload.DownloadItem.OutputPath.ToString()) .WriteSentryWarn("DownloadHistoryIncomplete") .Write(); } _logger.Debug("Not all episodes have been imported for {0}", trackedDownload.DownloadItem.Title); return(false); }
public bool VerifyImport(TrackedDownload trackedDownload, List <ImportResult> importResults) { var allMoviesImported = importResults.Where(c => c.Result == ImportResultType.Imported) .Select(c => c.ImportDecision.LocalMovie.Movie) .Any(); if (allMoviesImported) { _logger.Debug("All movies were imported for {0}", trackedDownload.DownloadItem.Title); trackedDownload.State = TrackedDownloadState.Imported; _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload, trackedDownload.RemoteMovie.Movie.Id)); return(true); } // Double check if all movies were imported by checking the history if at least one // file was imported. This will allow the decision engine to reject already imported // episode files and still mark the download complete when all files are imported. var atLeastOneMovieImported = importResults.Any(c => c.Result == ImportResultType.Imported); var historyItems = _historyService.FindByDownloadId(trackedDownload.DownloadItem.DownloadId) .OrderByDescending(h => h.Date) .ToList(); var allMoviesImportedInHistory = _trackedDownloadAlreadyImported.IsImported(trackedDownload, historyItems); if (allMoviesImportedInHistory) { // Log different error messages depending on the circumstances, but treat both as fully imported, because that's the reality. // The second message shouldn't be logged in most cases, but continued reporting would indicate an ongoing issue. if (atLeastOneMovieImported) { _logger.Debug("All movies were imported in history for {0}", trackedDownload.DownloadItem.Title); } else { _logger.Debug() .Message("No Movies were just imported, but all movies were previously imported, possible issue with download history.") .Property("MovieId", trackedDownload.RemoteMovie.Movie.Id) .Property("DownloadId", trackedDownload.DownloadItem.DownloadId) .Property("Title", trackedDownload.DownloadItem.Title) .Property("Path", trackedDownload.ImportItem.OutputPath.ToString()) .WriteSentryWarn("DownloadHistoryIncomplete") .Write(); } trackedDownload.State = TrackedDownloadState.Imported; _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload, trackedDownload.RemoteMovie.Movie.Id)); return(true); } _logger.Debug("Not all movies have been imported for {0}", trackedDownload.DownloadItem.Title); return(false); }
public bool VerifyImport(TrackedDownload trackedDownload, List<ImportResult> importResults) { var allItemsImported = importResults.Where(c => c.Result == ImportResultType.Imported) .Select(c => c.ImportDecision.Item.Book) .Count() >= Math.Max(1, trackedDownload.RemoteBook.Books.Count); if (allItemsImported) { trackedDownload.State = TrackedDownloadState.Imported; _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload)); return true; } // Double check if all episodes were imported by checking the history if at least one // file was imported. This will allow the decision engine to reject already imported // episode files and still mark the download complete when all files are imported. // EDGE CASE: This process relies on EpisodeIds being consistent between executions, if a series is updated // and an episode is removed, but later comes back with a different ID then Sonarr will treat it as incomplete. // Since imports should be relatively fast and these types of data changes are infrequent this should be quite // safe, but commenting for future benefit. if (importResults.Any(c => c.Result == ImportResultType.Imported)) { var historyItems = _historyService.FindByDownloadId(trackedDownload.DownloadItem.DownloadId) .OrderByDescending(h => h.Date) .ToList(); var allEpisodesImportedInHistory = _trackedDownloadAlreadyImported.IsImported(trackedDownload, historyItems); if (allEpisodesImportedInHistory) { trackedDownload.State = TrackedDownloadState.Imported; _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload)); return true; } } return false; }
public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, DownloadClientItem downloadItem) { var existingItem = Find(downloadItem.DownloadId); if (existingItem != null && existingItem.State != TrackedDownloadState.Downloading) { LogItemChange(existingItem, existingItem.DownloadItem, downloadItem); existingItem.DownloadItem = downloadItem; existingItem.IsTrackable = true; return(existingItem); } var trackedDownload = new TrackedDownload { DownloadClient = downloadClient.Id, DownloadItem = downloadItem, Protocol = downloadClient.Protocol, IsTrackable = true }; try { var parsedAlbumInfo = Parser.Parser.ParseAlbumTitle(trackedDownload.DownloadItem.Title); var historyItems = _historyService.FindByDownloadId(downloadItem.DownloadId) .OrderByDescending(h => h.Date) .ToList(); if (parsedAlbumInfo != null) { trackedDownload.RemoteAlbum = _parsingService.Map(parsedAlbumInfo); } if (historyItems.Any()) { var firstHistoryItem = historyItems.First(); var state = GetStateFromHistory(firstHistoryItem); // One potential issue here is if the latest is imported, but other episodes are ignored or never imported. // It's unlikely that will happen, but could happen if additional episodes are added to season after it's already imported. if (state == TrackedDownloadState.Imported) { var allImported = _trackedDownloadAlreadyImported.IsImported(trackedDownload, historyItems); trackedDownload.State = allImported ? TrackedDownloadState.Imported : TrackedDownloadState.Downloading; } else { trackedDownload.State = state; } if (firstHistoryItem.EventType == HistoryEventType.AlbumImportIncomplete) { var messages = Json.Deserialize <List <TrackedDownloadStatusMessage> >(firstHistoryItem?.Data["statusMessages"]).ToArray(); trackedDownload.Warn(messages); } var grabbedEvent = historyItems.FirstOrDefault(v => v.EventType == HistoryEventType.Grabbed); trackedDownload.Indexer = grabbedEvent?.Data["indexer"]; if (parsedAlbumInfo == null || trackedDownload.RemoteAlbum == null || trackedDownload.RemoteAlbum.Artist == null || trackedDownload.RemoteAlbum.Albums.Empty()) { // Try parsing the original source title and if that fails, try parsing it as a special // TODO: Pass the TVDB ID and TVRage IDs in as well so we have a better chance for finding the item var historyArtist = firstHistoryItem.Artist; var historyAlbums = new List <Album> { firstHistoryItem.Album }; parsedAlbumInfo = Parser.Parser.ParseAlbumTitle(firstHistoryItem.SourceTitle); if (parsedAlbumInfo != null) { trackedDownload.RemoteAlbum = _parsingService.Map(parsedAlbumInfo, firstHistoryItem.ArtistId, historyItems.Where(v => v.EventType == HistoryEventType.Grabbed).Select(h => h.AlbumId) .Distinct()); } else { parsedAlbumInfo = Parser.Parser.ParseAlbumTitleWithSearchCriteria(firstHistoryItem.SourceTitle, historyArtist, historyAlbums); if (parsedAlbumInfo != null) { trackedDownload.RemoteAlbum = _parsingService.Map(parsedAlbumInfo, firstHistoryItem.ArtistId, historyItems.Where(v => v.EventType == HistoryEventType.Grabbed).Select(h => h.AlbumId) .Distinct()); } } } } // Track it so it can be displayed in the queue even though we can't determine which artist it is for if (trackedDownload.RemoteAlbum == null) { _logger.Trace("No Album found for download '{0}'", trackedDownload.DownloadItem.Title); trackedDownload.Warn("No Album found for download '{0}'", trackedDownload.DownloadItem.Title); } } catch (Exception e) { _logger.Debug(e, "Failed to find album for " + downloadItem.Title); return(null); } LogItemChange(trackedDownload, existingItem?.DownloadItem, trackedDownload.DownloadItem); _cache.Set(trackedDownload.DownloadItem.DownloadId, trackedDownload); return(trackedDownload); }
public void Import(TrackedDownload trackedDownload) { SetImportItem(trackedDownload); if (!ValidatePath(trackedDownload)) { return; } trackedDownload.State = TrackedDownloadState.Importing; var outputPath = trackedDownload.ImportItem.OutputPath.FullPath; var importResults = _downloadedTracksImportService.ProcessPath(outputPath, ImportMode.Auto, trackedDownload.RemoteAlbum.Artist, trackedDownload.DownloadItem); if (importResults.Empty()) { trackedDownload.Warn("No files found are eligible for import in {0}", outputPath); return; } var allTracksImported = importResults.All(c => c.Result == ImportResultType.Imported) || importResults.Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, trackedDownload.RemoteAlbum.Albums.Sum(x => x.AlbumReleases.Value.Where(y => y.Monitored).Sum(z => z.TrackCount))); Console.WriteLine($"allimported: {allTracksImported}"); Console.WriteLine($"count: {importResults.Count(c => c.Result == ImportResultType.Imported)}"); Console.WriteLine($"max: {Math.Max(1, trackedDownload.RemoteAlbum.Albums.Sum(x => x.AlbumReleases.Value.Where(y => y.Monitored).Sum(z => z.TrackCount)))}"); if (allTracksImported) { trackedDownload.State = TrackedDownloadState.Imported; _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload, trackedDownload.RemoteAlbum.Artist.Id)); return; } // Double check if all albums were imported by checking the history if at least one // file was imported. This will allow the decision engine to reject already imported // albums and still mark the download complete when all files are imported. if (importResults.Any(c => c.Result == ImportResultType.Imported)) { var historyItems = _historyService.FindByDownloadId(trackedDownload.DownloadItem.DownloadId) .OrderByDescending(h => h.Date) .ToList(); var allTracksImportedInHistory = _trackedDownloadAlreadyImported.IsImported(trackedDownload, historyItems); if (allTracksImportedInHistory) { trackedDownload.State = TrackedDownloadState.Imported; _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload, trackedDownload.RemoteAlbum.Artist.Id)); return; } } trackedDownload.State = TrackedDownloadState.ImportPending; if (importResults.Empty()) { trackedDownload.Warn("No files found are eligible for import in {0}", outputPath); } if (importResults.Any(c => c.Result != ImportResultType.Imported)) { trackedDownload.State = TrackedDownloadState.ImportFailed; var statusMessages = importResults .Where(v => v.Result != ImportResultType.Imported) .Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.Item.Path), v.Errors)) .ToArray(); trackedDownload.Warn(statusMessages); _eventAggregator.PublishEvent(new AlbumImportIncompleteEvent(trackedDownload)); return; } }
public bool VerifyImport(TrackedDownload trackedDownload, List <ImportResult> importResults) { var allItemsImported = importResults.Where(c => c.Result == ImportResultType.Imported) .Select(c => c.ImportDecision.Item.Book) .Count() >= Math.Max(1, trackedDownload.RemoteBook?.Books.Count ?? 1); if (allItemsImported) { _logger.Debug("All books were imported for {0}", trackedDownload.DownloadItem.Title); trackedDownload.State = TrackedDownloadState.Imported; var importedAuthorId = importResults.Where(x => x.Result == ImportResultType.Imported) .Select(c => c.ImportDecision.Item.Author.Id) .MostCommon(); _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload, trackedDownload.RemoteBook?.Author.Id ?? importedAuthorId)); return(true); } // Double check if all episodes were imported by checking the history if at least one // file was imported. This will allow the decision engine to reject already imported // episode files and still mark the download complete when all files are imported. // EDGE CASE: This process relies on EpisodeIds being consistent between executions, if a series is updated // and an episode is removed, but later comes back with a different ID then Sonarr will treat it as incomplete. // Since imports should be relatively fast and these types of data changes are infrequent this should be quite // safe, but commenting for future benefit. var atLeastOneEpisodeImported = importResults.Any(c => c.Result == ImportResultType.Imported); var historyItems = _historyService.FindByDownloadId(trackedDownload.DownloadItem.DownloadId) .OrderByDescending(h => h.Date) .ToList(); var allEpisodesImportedInHistory = _trackedDownloadAlreadyImported.IsImported(trackedDownload, historyItems); if (allEpisodesImportedInHistory) { // Log different error messages depending on the circumstances, but treat both as fully imported, because that's the reality. // The second message shouldn't be logged in most cases, but continued reporting would indicate an ongoing issue. if (atLeastOneEpisodeImported) { _logger.Debug("All books were imported in history for {0}", trackedDownload.DownloadItem.Title); } else { _logger.Debug() .Message("No books were just imported, but all books were previously imported, possible issue with download history.") .Property("AuthorId", trackedDownload.RemoteBook.Author.Id) .Property("DownloadId", trackedDownload.DownloadItem.DownloadId) .Property("Title", trackedDownload.DownloadItem.Title) .Property("Path", trackedDownload.DownloadItem.OutputPath.ToString()) .WriteSentryWarn("DownloadHistoryIncomplete") .Write(); } trackedDownload.State = TrackedDownloadState.Imported; var importedAuthorId = historyItems.Where(x => x.EventType == EntityHistoryEventType.BookFileImported) .Select(x => x.AuthorId) .MostCommon(); _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload, trackedDownload.RemoteBook?.Author.Id ?? importedAuthorId)); return(true); } _logger.Debug("Not all books have been imported for {0}", trackedDownload.DownloadItem.Title); return(false); }