public List <ImportDecision> GetImportDecisions(List <string> videoFiles, Series series, DownloadClientItem downloadClientItem, ParsedEpisodeInfo folderInfo, bool sceneSource, bool filterExistingFiles)
        {
            var newFiles = filterExistingFiles ? _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series) : videoFiles.ToList();

            _logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count());

            ParsedEpisodeInfo downloadClientItemInfo = null;

            if (downloadClientItem != null)
            {
                downloadClientItemInfo = Parser.Parser.ParseTitle(downloadClientItem.Title);
            }

            // If not importing from a scene source (series folder for example), then assume all files are not samples
            // to avoid using media info on every file needlessly (especially if Analyse Media Files is disabled).
            var nonSampleVideoFileCount = sceneSource ? GetNonSampleVideoFileCount(newFiles, series, downloadClientItemInfo, folderInfo) : videoFiles.Count;

            var decisions = new List <ImportDecision>();

            foreach (var file in newFiles)
            {
                var localEpisode = new LocalEpisode
                {
                    Series = series,
                    DownloadClientEpisodeInfo = downloadClientItemInfo,
                    FolderEpisodeInfo         = folderInfo,
                    Path            = file,
                    SceneSource     = sceneSource,
                    ExistingFile    = series.Path.IsParentPath(file),
                    OtherVideoFiles = nonSampleVideoFileCount > 1
                };

                decisions.AddIfNotNull(GetDecision(localEpisode, downloadClientItem, nonSampleVideoFileCount > 1));
            }

            return(decisions);
        }
        private string GetOriginalFilePath(DownloadClientItem downloadClientItem, LocalEpisode localEpisode)
        {
            var path = localEpisode.Path;

            if (downloadClientItem != null && !downloadClientItem.OutputPath.IsEmpty)
            {
                var outputDirectory = downloadClientItem.OutputPath.Directory.ToString();

                if (outputDirectory.IsParentPath(path))
                {
                    return(outputDirectory.GetRelativePath(path));
                }
            }

            var folderEpisodeInfo = localEpisode.FolderEpisodeInfo;

            if (folderEpisodeInfo != null)
            {
                var folderPath = path.GetAncestorPath(folderEpisodeInfo.ReleaseTitle);

                if (folderPath != null)
                {
                    return(folderPath.GetParentPath().GetRelativePath(path));
                }
            }

            var parentPath      = path.GetParentPath();
            var grandparentPath = parentPath.GetParentPath();

            if (grandparentPath != null)
            {
                return(grandparentPath.GetRelativePath(path));
            }

            return(Path.GetFileName(path));
        }
Exemple #3
0
        public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
        {
            if (!_buildFileNames.RequiresEpisodeTitle(localEpisode.Series, localEpisode.Episodes))
            {
                _logger.Debug("File name format does not require episode title, skipping check");
                return(Decision.Accept());
            }

            foreach (var episode in localEpisode.Episodes)
            {
                var airDateUtc = episode.AirDateUtc;
                var title      = episode.Title;

                if (airDateUtc.HasValue && airDateUtc.Value.Before(DateTime.UtcNow.AddDays(-1)))
                {
                    _logger.Debug("Episode aired more than 1 day ago");
                    continue;
                }

                if (title.IsNullOrWhiteSpace())
                {
                    _logger.Debug("Episode does not have a title and recently aired");

                    return(Decision.Reject("Episode does not have a title and recently aired"));
                }

                if (title.Equals("TBA"))
                {
                    _logger.Debug("Episode has a TBA title and recently aired");

                    return(Decision.Reject("Episode has a TBA title and recently aired"));
                }
            }

            return(Decision.Accept());
        }
        public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
        {
            var episodeFiles = localEpisode.Episodes.Where(e => e.EpisodeFileId != 0).Select(e => e.EpisodeFile).ToList();

            if (episodeFiles.Count == 0)
            {
                _logger.Debug("No existing episode file, skipping");
                return(Decision.Accept());
            }

            if (episodeFiles.Count > 1)
            {
                _logger.Debug("More than one existing episode file, skipping.");
                return(Decision.Accept());
            }

            if (episodeFiles.First().Value.Size == localEpisode.Size)
            {
                _logger.Debug("'{0}' Has the same filesize as existing file", localEpisode.Path);
                return(Decision.Reject("Has the same filesize as existing file"));
            }

            return(Decision.Accept());
        }
        public LocalEpisode Augment(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
        {
            var isMediaFile = MediaFileExtensions.Extensions.Contains(Path.GetExtension(localEpisode.Path));

            if (localEpisode.DownloadClientEpisodeInfo == null &&
                localEpisode.FolderEpisodeInfo == null &&
                localEpisode.FileEpisodeInfo == null)
            {
                if (isMediaFile)
                {
                    throw new AugmentingFailedException("Unable to parse episode info from path: {0}", localEpisode.Path);
                }
            }

            localEpisode.Size      = _diskProvider.GetFileSize(localEpisode.Path);
            localEpisode.SceneName = localEpisode.SceneSource ? SceneNameCalculator.GetSceneName(localEpisode) : null;

            if (isMediaFile && (!localEpisode.ExistingFile || _configService.EnableMediaInfo))
            {
                localEpisode.MediaInfo = _videoFileInfoReader.GetMediaInfo(localEpisode.Path);
            }

            foreach (var augmenter in _augmenters)
            {
                try
                {
                    augmenter.Aggregate(localEpisode, downloadClientItem);
                }
                catch (Exception ex)
                {
                    _logger.Warn(ex, ex.Message);
                }
            }

            return(localEpisode);
        }
        public void Setup()
        {
            _localEpisode        = new LocalEpisode();
            _localEpisode.Series = new Series
            {
                Path = @"C:\Test\TV\Series".AsOsAgnostic()
            };

            _episodeFile = Builder <EpisodeFile>
                           .CreateNew()
                           .Build();

            Mocker.GetMock <IDiskProvider>()
            .Setup(c => c.FolderExists(Directory.GetParent(_localEpisode.Series.Path).FullName))
            .Returns(true);

            Mocker.GetMock <IDiskProvider>()
            .Setup(c => c.FileExists(It.IsAny <string>()))
            .Returns(true);

            Mocker.GetMock <IDiskProvider>()
            .Setup(c => c.GetParentFolder(It.IsAny <string>()))
            .Returns <string>(c => Path.GetDirectoryName(c));
        }
Exemple #7
0
        public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
        {
            if (downloadClientItem == null)
            {
                _logger.Debug("No download client information is available, skipping");
                return(Decision.Accept());
            }

            foreach (var episode in localEpisode.Episodes)
            {
                if (!episode.HasFile)
                {
                    _logger.Debug("Skipping already imported check for episode without file");
                    continue;
                }

                var episodeHistory = _historyService.FindByEpisodeId(episode.Id);
                var lastImported   = episodeHistory.FirstOrDefault(h => h.EventType == HistoryEventType.DownloadFolderImported);

                if (lastImported == null)
                {
                    continue;
                }

                // TODO: Ignore last imported check if the same release was grabbed again
                // See: https://github.com/Sonarr/Sonarr/issues/2393

                if (lastImported.DownloadId == downloadClientItem.DownloadId)
                {
                    _logger.Debug("Episode file previously imported at {0}", lastImported.Date);
                    return(Decision.Reject("Episode file already imported at {0}", lastImported.Date));
                }
            }

            return(Decision.Accept());
        }
        public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
        {
            if (localEpisode.ExistingFile)
            {
                _logger.Debug("Existing file, skipping sample check");
                return(Decision.Accept());
            }

            var sample = _detectSample.IsSample(localEpisode.Series,
                                                localEpisode.Path,
                                                localEpisode.IsSpecial);

            if (sample == DetectSampleResult.Sample)
            {
                return(Decision.Reject("Sample"));
            }

            else if (sample == DetectSampleResult.Indeterminate)
            {
                return(Decision.Reject("Unable to determine if file is a sample"));
            }

            return(Decision.Accept());
        }
Exemple #9
0
 public EpisodeImportedEvent(LocalEpisode episodeInfo, EpisodeFile importedEpisode, bool newDownload)
 {
     EpisodeInfo     = episodeInfo;
     ImportedEpisode = importedEpisode;
     NewDownload     = newDownload;
 }
 public EpisodeImportedEvent(LocalEpisode droppedEpisode, EpisodeFile importedEpisode)
 {
     DroppedEpisode  = droppedEpisode;
     ImportedEpisode = importedEpisode;
 }
        public override IEnumerable <ExtraFile> ProcessFiles(Series series, List <string> filesOnDisk, List <string> importedFiles)
        {
            _logger.Debug("Looking for existing metadata in {0}", series.Path);

            var metadataFiles = new List <MetadataFile>();
            var filterResult  = FilterAndClean(series, filesOnDisk, importedFiles);

            foreach (var possibleMetadataFile in filterResult.FilesOnDisk)
            {
                // Don't process files that have known Subtitle file extensions (saves a bit of unecessary processing)

                if (SubtitleFileExtensions.Extensions.Contains(Path.GetExtension(possibleMetadataFile)))
                {
                    continue;
                }

                foreach (var consumer in _consumers)
                {
                    var metadata = consumer.FindMetadataFile(series, possibleMetadataFile);

                    if (metadata == null)
                    {
                        continue;
                    }

                    if (metadata.Type == MetadataType.EpisodeImage ||
                        metadata.Type == MetadataType.EpisodeMetadata)
                    {
                        var localEpisode = new LocalEpisode
                        {
                            FileEpisodeInfo = Parser.Parser.ParsePath(possibleMetadataFile),
                            Series          = series,
                            Path            = possibleMetadataFile
                        };

                        try
                        {
                            _aggregationService.Augment(localEpisode, null);
                        }
                        catch (AugmentingFailedException)
                        {
                            _logger.Debug("Unable to parse extra file: {0}", possibleMetadataFile);
                            continue;
                        }

                        if (localEpisode.Episodes.Empty())
                        {
                            _logger.Debug("Cannot find related episodes for: {0}", possibleMetadataFile);
                            continue;
                        }

                        if (localEpisode.Episodes.DistinctBy(e => e.EpisodeFileId).Count() > 1)
                        {
                            _logger.Debug("Extra file: {0} does not match existing files.", possibleMetadataFile);
                            continue;
                        }

                        metadata.SeasonNumber  = localEpisode.SeasonNumber;
                        metadata.EpisodeFileId = localEpisode.Episodes.First().EpisodeFileId;
                    }

                    metadata.Extension = Path.GetExtension(possibleMetadataFile);

                    metadataFiles.Add(metadata);
                }
            }

            _logger.Info("Found {0} existing metadata files", metadataFiles.Count);
            _metadataFileService.Upsert(metadataFiles);

            // Return files that were just imported along with files that were
            // previously imported so previously imported files aren't imported twice

            return(metadataFiles.Concat(filterResult.PreviouslyImported));
        }
Exemple #12
0
        public void Execute(ManualImportCommand message)
        {
            _logger.ProgressTrace("Manually importing {0} files using mode {1}", message.Files.Count, message.ImportMode);

            var imported = new List <ImportResult>();
            var importedTrackedDownload = new List <ManuallyImportedFile>();

            for (int i = 0; i < message.Files.Count; i++)
            {
                _logger.ProgressTrace("Processing file {0} of {1}", i + 1, message.Files.Count);

                var file            = message.Files[i];
                var series          = _seriesService.GetSeries(file.SeriesId);
                var episodes        = _episodeService.GetEpisodes(file.EpisodeIds);
                var fileEpisodeInfo = Parser.Parser.ParsePath(file.Path) ?? new ParsedEpisodeInfo();
                var existingFile    = series.Path.IsParentPath(file.Path);

                TrackedDownload trackedDownload = null;

                var localEpisode = new LocalEpisode
                {
                    ExistingFile    = false,
                    Episodes        = episodes,
                    FileEpisodeInfo = fileEpisodeInfo,
                    Path            = file.Path,
                    ReleaseGroup    = file.ReleaseGroup,
                    Quality         = file.Quality,
                    Language        = file.Language,
                    Series          = series,
                    Size            = 0
                };

                if (file.DownloadId.IsNotNullOrWhiteSpace())
                {
                    trackedDownload = _trackedDownloadService.Find(file.DownloadId);
                    localEpisode.DownloadClientEpisodeInfo = trackedDownload?.RemoteEpisode?.ParsedEpisodeInfo;
                }

                if (file.FolderName.IsNotNullOrWhiteSpace())
                {
                    localEpisode.FolderEpisodeInfo = Parser.Parser.ParseTitle(file.FolderName);
                    localEpisode.SceneSource       = !existingFile;
                }

                // Augment episode file so imported files have all additional information an automatic import would
                localEpisode = _aggregationService.Augment(localEpisode, trackedDownload?.DownloadItem);

                // Apply the user-chosen values.
                localEpisode.Series       = series;
                localEpisode.Episodes     = episodes;
                localEpisode.ReleaseGroup = file.ReleaseGroup;
                localEpisode.Quality      = file.Quality;
                localEpisode.Language     = file.Language;

                //TODO: Cleanup non-tracked downloads

                var importDecision = new ImportDecision(localEpisode);

                if (trackedDownload == null)
                {
                    imported.AddRange(_importApprovedEpisodes.Import(new List <ImportDecision> {
                        importDecision
                    }, !existingFile, null, message.ImportMode));
                }
                else
                {
                    var importResult = _importApprovedEpisodes.Import(new List <ImportDecision> {
                        importDecision
                    }, true, trackedDownload.DownloadItem, message.ImportMode).First();

                    imported.Add(importResult);

                    importedTrackedDownload.Add(new ManuallyImportedFile
                    {
                        TrackedDownload = trackedDownload,
                        ImportResult    = importResult
                    });
                }
            }

            if (imported.Any())
            {
                _logger.ProgressTrace("Manually imported {0} files", imported.Count);
            }

            foreach (var groupedTrackedDownload in importedTrackedDownload.GroupBy(i => i.TrackedDownload.DownloadItem.DownloadId).ToList())
            {
                var trackedDownload = groupedTrackedDownload.First().TrackedDownload;
                var importedSeries  = imported.First().ImportDecision.LocalEpisode.Series;
                var outputPath      = trackedDownload.ImportItem.OutputPath.FullPath;

                if (_diskProvider.FolderExists(outputPath))
                {
                    if (_downloadedEpisodesImportService.ShouldDeleteFolder(
                            new DirectoryInfo(outputPath), importedSeries) &&
                        trackedDownload.DownloadItem.CanMoveFiles)
                    {
                        _diskProvider.DeleteFolder(outputPath, true);
                    }
                }

                var allEpisodesImported = groupedTrackedDownload.Select(c => c.ImportResult)
                                          .Where(c => c.Result == ImportResultType.Imported)
                                          .SelectMany(c => c.ImportDecision.LocalEpisode.Episodes).Count() >=
                                          Math.Max(1, trackedDownload.RemoteEpisode?.Episodes?.Count ?? 1);

                if (allEpisodesImported)
                {
                    trackedDownload.State = TrackedDownloadState.Imported;
                    _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload, importedSeries.Id));
                }
            }
        }
 public EpisodeDownloadedEvent(LocalEpisode episode)
 {
     Episode = episode;
 }
        public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
        {
            if (localEpisode.ExistingFile)
            {
                _logger.Debug("{0} is in series folder, skipping check", localEpisode.Path);
                return(Decision.Accept());
            }

            var episodeTitleRequired = _configService.EpisodeTitleRequired;

            if (episodeTitleRequired == EpisodeTitleRequiredType.Never)
            {
                _logger.Debug("Episode titles are never required, skipping check");
                return(Decision.Accept());
            }

            if (!_buildFileNames.RequiresEpisodeTitle(localEpisode.Series, localEpisode.Episodes))
            {
                _logger.Debug("File name format does not require episode title, skipping check");
                return(Decision.Accept());
            }

            var episodes                = localEpisode.Episodes;
            var firstEpisode            = episodes.First();
            var episodesInSeason        = _episodeService.GetEpisodesBySeason(firstEpisode.SeriesId, firstEpisode.EpisodeNumber);
            var allEpisodesOnTheSameDay = firstEpisode.AirDateUtc.HasValue && episodes.All(e =>
                                                                                           !e.AirDateUtc.HasValue ||
                                                                                           e.AirDateUtc.Value == firstEpisode.AirDateUtc.Value);

            if (episodeTitleRequired == EpisodeTitleRequiredType.BulkSeasonReleases &&
                allEpisodesOnTheSameDay &&
                episodesInSeason.Count(e => !e.AirDateUtc.HasValue ||
                                       e.AirDateUtc.Value == firstEpisode.AirDateUtc.Value
                                       ) < 4
                )
            {
                _logger.Debug("Episode title only required for bulk season releases");
                return(Decision.Accept());
            }

            foreach (var episode in episodes)
            {
                var airDateUtc = episode.AirDateUtc;
                var title      = episode.Title;

                if (airDateUtc.HasValue && airDateUtc.Value.Before(DateTime.UtcNow.AddHours(-48)))
                {
                    _logger.Debug("Episode aired more than 48 hours ago");
                    continue;
                }

                if (title.IsNullOrWhiteSpace())
                {
                    _logger.Debug("Episode does not have a title and recently aired");

                    return(Decision.Reject("Episode does not have a title and recently aired"));
                }

                if (title.Equals("TBA"))
                {
                    _logger.Debug("Episode has a TBA title and recently aired");

                    return(Decision.Reject("Episode has a TBA title and recently aired"));
                }
            }

            return(Decision.Accept());
        }
        public override IEnumerable <ExtraFile> ImportFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, List <string> files, bool isReadOnly)
        {
            var importedFiles  = new List <ExtraFile>();
            var filteredFiles  = files.Where(f => CanImportFile(localEpisode, episodeFile, f, Path.GetExtension(f), isReadOnly)).ToList();
            var sourcePath     = localEpisode.Path;
            var sourceFolder   = _diskProvider.GetParentFolder(sourcePath);
            var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
            var matchingFiles  = new List <string>();
            var hasNfo         = false;

            foreach (var file in filteredFiles)
            {
                try
                {
                    // Filter out duplicate NFO files
                    if (file.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (hasNfo)
                        {
                            continue;
                        }

                        hasNfo = true;
                    }

                    // Filename match
                    if (Path.GetFileNameWithoutExtension(file).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        matchingFiles.Add(file);
                        continue;
                    }

                    // Season and episode match
                    var fileEpisodeInfo = Parser.Parser.ParsePath(file) ?? new ParsedEpisodeInfo();

                    if (fileEpisodeInfo.EpisodeNumbers.Length == 0)
                    {
                        continue;
                    }

                    if (fileEpisodeInfo.SeasonNumber == localEpisode.FileEpisodeInfo.SeasonNumber &&
                        fileEpisodeInfo.EpisodeNumbers.SequenceEqual(localEpisode.FileEpisodeInfo.EpisodeNumbers))
                    {
                        matchingFiles.Add(file);
                    }
                }
                catch (Exception ex)
                {
                    _logger.Warn(ex, "Failed to import extra file: {0}", file);
                }
            }

            foreach (string file in matchingFiles)
            {
                try
                {
                    var extraFile = ImportFile(localEpisode.Series, episodeFile, file, isReadOnly, Path.GetExtension(file), null);
                    _mediaFileAttributeService.SetFilePermissions(file);
                    _otherExtraFileService.Upsert(extraFile);
                    importedFiles.Add(extraFile);
                }
                catch (Exception ex)
                {
                    _logger.Warn(ex, "Failed to import extra file: {0}", file);
                }
            }

            return(importedFiles);
        }
 public EpisodeDownloadedEvent(LocalEpisode episode, EpisodeFile episodeFile, List <EpisodeFile> oldFiles)
 {
     Episode     = episode;
     EpisodeFile = episodeFile;
     OldFiles    = oldFiles;
 }
        private ImportDecision GetDecision(string file, Series series, DownloadClientItem downloadClientItem, ParsedEpisodeInfo folderInfo, bool sceneSource, bool shouldUseFolderName)
        {
            ImportDecision decision = null;

            try
            {
                var localEpisode = _parsingService.GetLocalEpisode(file, series, shouldUseFolderName ? folderInfo : null, sceneSource);

                if (localEpisode != null)
                {
                    localEpisode.Quality = GetQuality(folderInfo, localEpisode.Quality, series);
                    localEpisode.Size    = _diskProvider.GetFileSize(file);

                    _logger.Debug("Size: {0}", localEpisode.Size);

                    //TODO: make it so media info doesn't ruin the import process of a new series
                    if (sceneSource)
                    {
                        localEpisode.MediaInfo = _videoFileInfoReader.GetMediaInfo(file);
                    }

                    if (localEpisode.Episodes.Empty())
                    {
                        if (localEpisode.ParsedEpisodeInfo.IsPartialSeason)
                        {
                            decision = new ImportDecision(localEpisode, new Rejection("Partial season packs are not supported"));
                        }
                        else if (localEpisode.ParsedEpisodeInfo.IsSeasonExtra)
                        {
                            decision = new ImportDecision(localEpisode, new Rejection("Extras are not supported"));
                        }
                        else
                        {
                            decision = new ImportDecision(localEpisode, new Rejection("Invalid season or episode"));
                        }
                    }
                    else
                    {
                        decision = GetDecision(localEpisode, downloadClientItem);
                    }
                }

                else
                {
                    localEpisode      = new LocalEpisode();
                    localEpisode.Path = file;

                    decision = new ImportDecision(localEpisode, new Rejection("Unable to parse file"));
                }
            }
            catch (Exception e)
            {
                _logger.Error(e, "Couldn't import file. {0}", file);

                var localEpisode = new LocalEpisode {
                    Path = file
                };
                decision = new ImportDecision(localEpisode, new Rejection("Unexpected error processing file"));
            }

            if (decision == null)
            {
                _logger.Error("Unable to make a decision on {0}", file);
            }
            else if (decision.Rejections.Any())
            {
                _logger.Debug("File rejected for the following reasons: {0}", string.Join(", ", decision.Rejections));
            }
            else
            {
                _logger.Debug("File accepted");
            }

            return(decision);
        }
        public void Execute(ManualImportCommand message)
        {
            _logger.ProgressTrace("Manually importing {0} files", message.Files.Count);

            var imported = new List <ImportResult>();
            var importedTrackedDownload = new List <ManuallyImportedFile>();

            for (int i = 0; i < message.Files.Count; i++)
            {
                _logger.ProgressTrace("Processing file {0} of {1}", i + 1, message.Files.Count);

                var file              = message.Files[i];
                var series            = _seriesService.GetSeries(file.SeriesId);
                var episodes          = _episodeService.GetEpisodes(file.EpisodeIds);
                var parsedEpisodeInfo = Parser.Parser.ParsePath(file.Path) ?? new ParsedEpisodeInfo();
                var mediaInfo         = _videoFileInfoReader.GetMediaInfo(file.Path);
                var existingFile      = series.Path.IsParentPath(file.Path);

                var localEpisode = new LocalEpisode
                {
                    ExistingFile      = false,
                    Episodes          = episodes,
                    MediaInfo         = mediaInfo,
                    ParsedEpisodeInfo = parsedEpisodeInfo,
                    Path    = file.Path,
                    Quality = file.Quality,
                    Series  = series,
                    Size    = 0
                };

                //TODO: Option to copy instead of import
                //TODO: Cleanup non-tracked downloads

                var importDecision = new ImportDecision(localEpisode);

                if (file.DownloadId.IsNullOrWhiteSpace())
                {
                    imported.AddRange(_importApprovedEpisodes.Import(new List <ImportDecision> {
                        importDecision
                    }, !existingFile));
                }

                else
                {
                    var trackedDownload = _trackedDownloadService.Find(file.DownloadId);
                    var importResult    = _importApprovedEpisodes.Import(new List <ImportDecision> {
                        importDecision
                    }, true, trackedDownload.DownloadItem).First();

                    imported.Add(importResult);

                    importedTrackedDownload.Add(new ManuallyImportedFile
                    {
                        TrackedDownload = trackedDownload,
                        ImportResult    = importResult
                    });
                }
            }

            _logger.ProgressTrace("Manually imported {0} files", imported.Count);

            foreach (var groupedTrackedDownload in importedTrackedDownload.GroupBy(i => i.TrackedDownload.DownloadItem.DownloadId).ToList())
            {
                var trackedDownload = groupedTrackedDownload.First().TrackedDownload;

                if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath.FullPath))
                {
                    if (_downloadedEpisodesImportService.ShouldDeleteFolder(
                            new DirectoryInfo(trackedDownload.DownloadItem.OutputPath.FullPath),
                            trackedDownload.RemoteEpisode.Series) && !trackedDownload.DownloadItem.IsReadOnly)
                    {
                        _diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath.FullPath, true);
                    }
                }

                if (groupedTrackedDownload.Select(c => c.ImportResult).Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, trackedDownload.RemoteEpisode.Episodes.Count))
                {
                    trackedDownload.State = TrackedDownloadStage.Imported;
                    _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
                }
            }
        }
Exemple #19
0
 public ImportDecision(LocalEpisode localEpisode, params Rejection[] rejections)
 {
     LocalEpisode = localEpisode;
     Rejections   = rejections.ToList();
 }
        public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
        {
            localEpisode.Episodes = GetEpisodes(localEpisode);

            return(localEpisode);
        }
Exemple #21
0
        public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
        {
            var source               = QualitySource.Unknown;
            var sourceConfidence     = Confidence.Default;
            var resolution           = 0;
            var resolutionConfidence = Confidence.Default;
            var revision             = new Revision(1);
            var revisionConfidence   = Confidence.Default;

            foreach (var augmentQuality in _augmentQualities)
            {
                var augmentedQuality = augmentQuality.AugmentQuality(localEpisode, downloadClientItem);
                if (augmentedQuality == null)
                {
                    continue;
                }

                _logger.Trace("Considering Source {0} ({1}) Resolution {2} ({3}) Revision {4} from {5}", augmentedQuality.Source, augmentedQuality.SourceConfidence, augmentedQuality.Resolution, augmentedQuality.ResolutionConfidence, augmentedQuality.Revision, augmentQuality.Name);

                if (source == QualitySource.Unknown ||
                    augmentedQuality.SourceConfidence > sourceConfidence && augmentedQuality.Source != QualitySource.Unknown)
                {
                    source           = augmentedQuality.Source;
                    sourceConfidence = augmentedQuality.SourceConfidence;
                }

                if (resolution == 0 ||
                    augmentedQuality.ResolutionConfidence > resolutionConfidence && augmentedQuality.Resolution > 0)
                {
                    resolution           = augmentedQuality.Resolution;
                    resolutionConfidence = augmentedQuality.ResolutionConfidence;
                }

                if (augmentedQuality.Revision != null)
                {
                    // Update the revision and confidence if it is higher than the current confidence,
                    // this will allow explicitly detected v0 to override the v1 default.
                    if (augmentedQuality.RevisionConfidence > revisionConfidence)
                    {
                        revision           = augmentedQuality.Revision;
                        revisionConfidence = augmentedQuality.RevisionConfidence;
                    }
                    // Update the revision and confidence if it is the same confidence and the revision is higher,
                    // this will allow the best revision to be used in the event there is a disagreement.
                    else if (augmentedQuality.RevisionConfidence == revisionConfidence &&
                             augmentedQuality.Revision > revision)
                    {
                        revision           = augmentedQuality.Revision;
                        revisionConfidence = augmentedQuality.RevisionConfidence;
                    }
                }
            }

            _logger.Trace("Selected Source {0} ({1}) Resolution {2} ({3}) Revision {4}", source, sourceConfidence, resolution, resolutionConfidence, revision);

            var quality = new QualityModel(QualityFinder.FindBySourceAndResolution(source, resolution), revision);

            if (resolutionConfidence == Confidence.MediaInfo)
            {
                quality.ResolutionDetectionSource = QualityDetectionSource.MediaInfo;
            }
            else if (resolutionConfidence == Confidence.Fallback)
            {
                quality.ResolutionDetectionSource = QualityDetectionSource.Extension;
            }
            else
            {
                quality.ResolutionDetectionSource = QualityDetectionSource.Name;
            }

            if (sourceConfidence == Confidence.Fallback)
            {
                quality.SourceDetectionSource = QualityDetectionSource.Extension;
            }
            else
            {
                quality.SourceDetectionSource = QualityDetectionSource.Name;
            }

            quality.RevisionDetectionSource = revisionConfidence == Confidence.Tag ? QualityDetectionSource.Name : QualityDetectionSource.Unknown;

            _logger.Debug("Using quality: {0}", quality);

            localEpisode.Quality = quality;

            return(localEpisode);
        }
Exemple #22
0
 public abstract IEnumerable <ExtraFile> ImportFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, List <string> files, bool isReadOnly);
 private void EnsureEpisodeFolder(EpisodeFile episodeFile, LocalEpisode localEpisode, string filePath)
 {
     EnsureEpisodeFolder(episodeFile, localEpisode.Series, localEpisode.SeasonNumber, filePath);
 }
 public override bool CanImportFile(LocalEpisode localEpisode, EpisodeFile episodeFile, string path, string extension, bool readOnly)
 {
     return(false);
 }
Exemple #25
0
        private ManualImportItem ProcessFile(string rootFolder, string baseFolder, string file, string downloadId, Series series = null)
        {
            try
            {
                var trackedDownload = GetTrackedDownload(downloadId);
                var relativeFile    = baseFolder.GetRelativePath(file);

                if (series == null)
                {
                    _parsingService.GetSeries(relativeFile.Split('\\', '/')[0]);
                }

                if (series == null)
                {
                    series = _parsingService.GetSeries(relativeFile);
                }

                if (trackedDownload != null && series == null)
                {
                    series = trackedDownload?.RemoteEpisode?.Series;
                }

                if (series == null)
                {
                    var relativeParseInfo = Parser.Parser.ParsePath(relativeFile);

                    if (relativeParseInfo != null)
                    {
                        series = _seriesService.FindByTitle(relativeParseInfo.SeriesTitle);
                    }
                }

                if (series == null)
                {
                    var localEpisode = new LocalEpisode();
                    localEpisode.Path         = file;
                    localEpisode.ReleaseGroup = Parser.Parser.ParseReleaseGroup(file);
                    localEpisode.Quality      = QualityParser.ParseQuality(file);
                    localEpisode.Language     = LanguageParser.ParseLanguage(file);
                    localEpisode.Size         = _diskProvider.GetFileSize(file);

                    return(MapItem(new ImportDecision(localEpisode, new Rejection("Unknown Series")), rootFolder,
                                   downloadId, null));
                }

                var importDecisions = _importDecisionMaker.GetImportDecisions(new List <string> {
                    file
                }, series,
                                                                              trackedDownload?.DownloadItem, null, SceneSource(series, baseFolder));

                if (importDecisions.Any())
                {
                    return(MapItem(importDecisions.First(), rootFolder, downloadId, null));
                }
            }
            catch (Exception ex)
            {
                _logger.Warn(ex, "Failed to process file: {0}", file);
            }

            return(new ManualImportItem
            {
                DownloadId = downloadId,
                Path = file,
                RelativePath = rootFolder.GetRelativePath(file),
                Name = Path.GetFileNameWithoutExtension(file),
                Rejections = new List <Rejection>()
            });
        }
 public override IEnumerable <ExtraFile> ImportFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, List <string> files, bool isReadOnly)
 {
     return(Enumerable.Empty <ExtraFile>());
 }
        public LocalEpisode Aggregate(LocalEpisode localEpisode, bool otherFiles)
        {
            localEpisode.Episodes = GetEpisodes(localEpisode, otherFiles);

            return(localEpisode);
        }
Exemple #28
0
        private ManualImportItem ProcessFile(string rootFolder, string baseFolder, string file, string downloadId)
        {
            DownloadClientItem downloadClientItem = null;
            var relativeFile = baseFolder.GetRelativePath(file);
            var series       = _parsingService.GetSeries(relativeFile.Split('\\', '/')[0]);

            if (series == null)
            {
                series = _parsingService.GetSeries(relativeFile);
            }

            if (downloadId.IsNotNullOrWhiteSpace())
            {
                var trackedDownload = _trackedDownloadService.Find(downloadId);
                downloadClientItem = trackedDownload.DownloadItem;

                if (series == null)
                {
                    series = trackedDownload.RemoteEpisode.Series;
                }
            }

            if (series == null)
            {
                var relativeParseInfo = Parser.Parser.ParsePath(relativeFile);

                if (relativeParseInfo != null)
                {
                    series = _seriesService.FindByTitle(relativeParseInfo.SeriesTitle);
                }
            }

            if (series == null)
            {
                var localEpisode = new LocalEpisode();
                localEpisode.Path    = file;
                localEpisode.Quality = QualityParser.ParseQuality(file);
                localEpisode.Size    = _diskProvider.GetFileSize(file);

                return(MapItem(new ImportDecision(localEpisode, new Rejection("Unknown Series")), rootFolder, downloadId, null));
            }

            var importDecisions = _importDecisionMaker.GetImportDecisions(new List <string> {
                file
            },
                                                                          series, downloadClientItem, null, SceneSource(series, baseFolder));

            if (importDecisions.Any())
            {
                return(MapItem(importDecisions.First(), rootFolder, downloadId, null));
            }

            return(new ManualImportItem
            {
                DownloadId = downloadId,
                Path = file,
                RelativePath = rootFolder.GetRelativePath(file),
                Name = Path.GetFileNameWithoutExtension(file),
                Rejections = new List <Rejection>()
            });
        }
        public override IEnumerable <ExtraFile> ProcessFiles(Series series, List <string> filesOnDisk, List <string> importedFiles)
        {
            _logger.Debug("Looking for existing extra files in {0}", series.Path);

            var extraFiles   = new List <OtherExtraFile>();
            var filterResult = FilterAndClean(series, filesOnDisk, importedFiles);

            foreach (var possibleExtraFile in filterResult.FilesOnDisk)
            {
                var extension = Path.GetExtension(possibleExtraFile);

                if (extension.IsNullOrWhiteSpace())
                {
                    _logger.Debug("No extension for file: {0}", possibleExtraFile);
                    continue;
                }

                var localEpisode = new LocalEpisode
                {
                    FileEpisodeInfo = Parser.Parser.ParsePath(possibleExtraFile),
                    Series          = series,
                    Path            = possibleExtraFile
                };

                try
                {
                    _aggregationService.Augment(localEpisode, null);
                }
                catch (AugmentingFailedException)
                {
                    _logger.Debug("Unable to parse extra file: {0}", possibleExtraFile);
                    continue;
                }

                if (localEpisode.Episodes.Empty())
                {
                    _logger.Debug("Cannot find related episodes for: {0}", possibleExtraFile);
                    continue;
                }

                if (localEpisode.Episodes.DistinctBy(e => e.EpisodeFileId).Count() > 1)
                {
                    _logger.Debug("Extra file: {0} does not match existing files.", possibleExtraFile);
                    continue;
                }

                var extraFile = new OtherExtraFile
                {
                    SeriesId      = series.Id,
                    SeasonNumber  = localEpisode.SeasonNumber,
                    EpisodeFileId = localEpisode.Episodes.First().EpisodeFileId,
                    RelativePath  = series.Path.GetRelativePath(possibleExtraFile),
                    Extension     = extension
                };

                extraFiles.Add(extraFile);
            }

            _logger.Info("Found {0} existing other extra files", extraFiles.Count);
            _otherExtraFileService.Upsert(extraFiles);

            // Return files that were just imported along with files that were
            // previously imported so previously imported files aren't imported twice

            return(extraFiles.Concat(filterResult.PreviouslyImported));
        }
Exemple #30
0
        public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem, bool otherFiles)
        {
            localEpisode.Episodes = GetEpisodes(localEpisode, otherFiles);

            return(localEpisode);
        }