private RemoteAlbum GivenRemoteAlbum(List <Album> albums, QualityModel quality, int age = 0, long size = 0, DownloadProtocol downloadProtocol = DownloadProtocol.Usenet)
        {
            var remoteAlbum = new RemoteAlbum();

            remoteAlbum.ParsedAlbumInfo         = new ParsedAlbumInfo();
            remoteAlbum.ParsedAlbumInfo.Quality = quality;

            remoteAlbum.Albums = new List <Album>();
            remoteAlbum.Albums.AddRange(albums);

            remoteAlbum.Release                  = new ReleaseInfo();
            remoteAlbum.Release.PublishDate      = DateTime.Now.AddDays(-age);
            remoteAlbum.Release.Size             = size;
            remoteAlbum.Release.DownloadProtocol = downloadProtocol;

            remoteAlbum.Artist = Builder <Artist> .CreateNew()
                                 .With(e => e.QualityProfile = new QualityProfile
            {
                Items = Qualities.QualityFixture.GetDefaultQualities()
            }).Build();

            remoteAlbum.DownloadAllowed = true;

            return(remoteAlbum);
        }
예제 #2
0
        public virtual Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
        {
            if (subject.Release.DownloadProtocol != Indexers.DownloadProtocol.Usenet)
            {
                _logger.Debug("Not checking minimum age requirement for non-usenet report");
                return(Decision.Accept());
            }

            var age        = subject.Release.AgeMinutes;
            var minimumAge = _configService.MinimumAge;
            var ageRounded = Math.Round(age, 1);

            if (minimumAge == 0)
            {
                _logger.Debug("Minimum age is not set.");
                return(Decision.Accept());
            }


            _logger.Debug("Checking if report meets minimum age requirements. {0}", ageRounded);

            if (age < minimumAge)
            {
                _logger.Debug("Only {0} minutes old, minimum age is {1} minutes", ageRounded, minimumAge);
                return(Decision.Reject("Only {0} minutes old, minimum age is {1} minutes", ageRounded, minimumAge));
            }

            _logger.Debug("Release is {0} minutes old, greater than minimum age of {1} minutes", ageRounded, minimumAge);

            return(Decision.Accept());
        }
예제 #3
0
        private string DownloadFromMagnetUrl(RemoteAlbum remoteAlbum, string magnetUrl)
        {
            string hash       = null;
            string actualHash = null;

            try
            {
                hash = new MagnetLink(magnetUrl).InfoHash.ToHex();
            }
            catch (FormatException ex)
            {
                _logger.Error(ex, "Failed to parse magnetlink for release '{0}': '{1}'", remoteAlbum.Release.Title, magnetUrl);

                return(null);
            }

            if (hash != null)
            {
                actualHash = AddFromMagnetLink(remoteAlbum, hash, magnetUrl);
            }

            if (actualHash.IsNotNullOrWhiteSpace() && hash != actualHash)
            {
                _logger.Debug(
                    "{0} did not return the expected InfoHash for '{1}', Lidarr could potentially lose track of the download in progress.",
                    Definition.Implementation, remoteAlbum.Release.DownloadUrl);
            }

            return(actualHash);
        }
        public void Setup()
        {
            var artist = Builder <Artist> .CreateNew().With(s => s.Id = 1234).Build();

            _remoteAlbum = new RemoteAlbum
            {
                ParsedAlbumInfo = new ParsedAlbumInfo
                {
                    Discography = true
                },
                Albums = Builder <Album> .CreateListOfSize(3)
                         .All()
                         .With(e => e.ReleaseDate = DateTime.UtcNow.AddDays(-8))
                         .With(s => s.ArtistId    = artist.Id)
                         .BuildList(),
                Artist  = artist,
                Release = new ReleaseInfo
                {
                    Title = "Artist.Discography.1978.2005.FLAC-RlsGrp"
                }
            };

            Mocker.GetMock <IAlbumService>().Setup(s => s.AlbumsBetweenDates(It.IsAny <DateTime>(), It.IsAny <DateTime>(), false))
            .Returns(new List <Album>());
        }
예제 #5
0
        public void Setup()
        {
            _artist = Builder <Artist> .CreateNew().With(s => s.Id = 1).Build();

            _remoteAlbum = new RemoteAlbum
            {
                Artist  = _artist,
                Release = new TorrentInfo
                {
                    IndexerId = 1,
                    Title     = "Artist - Album [FLAC-RlsGrp]",
                    Seeders   = 0
                }
            };

            _indexerDefinition = new IndexerDefinition
            {
                Settings = new TorrentRssIndexerSettings {
                    MinimumSeeders = 5
                }
            };

            Mocker.GetMock <IIndexerFactory>()
            .Setup(v => v.Get(1))
            .Returns(_indexerDefinition);
        }
예제 #6
0
        public void Setup()
        {
            _downloadClients = new List <IDownloadClient>();

            Mocker.GetMock <IProvideDownloadClient>()
            .Setup(v => v.GetDownloadClients())
            .Returns(_downloadClients);

            Mocker.GetMock <IProvideDownloadClient>()
            .Setup(v => v.GetDownloadClient(It.IsAny <DownloadProtocol>()))
            .Returns <DownloadProtocol>(v => _downloadClients.FirstOrDefault(d => d.Protocol == v));

            var episodes = Builder <Album> .CreateListOfSize(2)
                           .TheFirst(1).With(s => s.Id = 12)
                           .TheNext(1).With(s => s.Id  = 99)
                           .All().With(s => s.ArtistId = 5)
                           .Build().ToList();

            var releaseInfo = Builder <ReleaseInfo> .CreateNew()
                              .With(v => v.DownloadProtocol = DownloadProtocol.Usenet)
                              .With(v => v.DownloadUrl      = "http://test.site/download1.ext")
                              .Build();

            _parseResult = Builder <RemoteAlbum> .CreateNew()
                           .With(c => c.Artist  = Builder <Artist> .CreateNew().Build())
                           .With(c => c.Release = releaseInfo)
                           .With(c => c.Albums  = episodes)
                           .Build();
        }
        public virtual Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
        {
            var qualityProfile = subject.Artist.QualityProfile.Value;

            foreach (var album in subject.Albums)
            {
                var tracksMissing = _missingFilesCache.Get(album.Id.ToString(),
                                                           () => _trackService.TracksWithoutFiles(album.Id).Any(),
                                                           TimeSpan.FromSeconds(30));

                var trackFiles = _mediaFileService.GetFilesByAlbum(album.Id);

                if (!tracksMissing && trackFiles.Any())
                {
                    // Get a distinct list of all current track qualities for a given album
                    var currentQualities = trackFiles.Select(c => c.Quality).Distinct().ToList();

                    _logger.Debug("Comparing file quality with report. Existing files contain {0}", currentQualities.ConcatToString());

                    if (!_upgradableSpecification.IsUpgradeAllowed(qualityProfile,
                                                                   currentQualities,
                                                                   subject.ParsedAlbumInfo.Quality))
                    {
                        _logger.Debug("Upgrading is not allowed by the quality profile");

                        return(Decision.Reject("Existing files and the Quality profile does not allow upgrades"));
                    }
                }
            }

            return(Decision.Accept());
        }
예제 #8
0
        public void Setup()
        {
            var completed = Builder <DownloadClientItem> .CreateNew()
                            .With(h => h.Status     = DownloadItemStatus.Completed)
                            .With(h => h.OutputPath = new OsPath(@"C:\DropFolder\MyDownload".AsOsAgnostic()))
                            .With(h => h.Title      = "Drone.S01E01.HDTV")
                            .Build();

            _grabHistory = Builder <History.History> .CreateListOfSize(2).BuildList();

            var remoteAlbum = new RemoteAlbum
            {
                Artist = new Artist(),
                Albums = new List <Album> {
                    new Album {
                        Id = 1
                    }
                }
            };

            _trackedDownload = Builder <TrackedDownload> .CreateNew()
                               .With(c => c.State        = TrackedDownloadState.DownloadFailedPending)
                               .With(c => c.DownloadItem = completed)
                               .With(c => c.RemoteAlbum  = remoteAlbum)
                               .Build();

            Mocker.GetMock <IHistoryService>()
            .Setup(s => s.Find(_trackedDownload.DownloadItem.DownloadId, HistoryEventType.Grabbed))
            .Returns(_grabHistory);
        }
예제 #9
0
        public virtual Decision IsSatisfiedBy(RemoteAlbum remoteAlbum, SearchCriteriaBase searchCriteria)
        {
            if (searchCriteria == null)
            {
                return(Decision.Accept());
            }

            var singleAlbumSpec = searchCriteria as AlbumSearchCriteria;

            if (singleAlbumSpec == null)
            {
                return(Decision.Accept());
            }

            if (Parser.Parser.CleanArtistName(singleAlbumSpec.AlbumTitle) != Parser.Parser.CleanArtistName(remoteAlbum.ParsedAlbumInfo.AlbumTitle))
            {
                _logger.Debug("Album does not match searched album title, skipping.");
                return(Decision.Reject("Wrong album"));
            }

            if (!remoteAlbum.ParsedAlbumInfo.AlbumTitle.Any())
            {
                _logger.Debug("Full discography result during single album search, skipping.");
                return(Decision.Reject("Full artist pack"));
            }

            return(Decision.Accept());
        }
예제 #10
0
        private void RemoveGrabbed(RemoteAlbum remoteAlbum)
        {
            var pendingReleases = GetPendingReleases(remoteAlbum.Artist.Id);
            var albumIds        = remoteAlbum.Albums.Select(e => e.Id);

            var existingReports = pendingReleases.Where(r => r.RemoteAlbum.Albums.Select(e => e.Id)
                                                        .Intersect(albumIds)
                                                        .Any())
                                  .ToList();

            if (existingReports.Empty())
            {
                return;
            }

            var profile = remoteAlbum.Artist.QualityProfile.Value;

            foreach (var existingReport in existingReports)
            {
                var compare = new QualityModelComparer(profile).Compare(remoteAlbum.ParsedAlbumInfo.Quality,
                                                                        existingReport.RemoteAlbum.ParsedAlbumInfo.Quality);

                //Only remove lower/equal quality pending releases
                //It is safer to retry these releases on the next round than remove it and try to re-add it (if its still in the feed)
                if (compare >= 0)
                {
                    _logger.Debug("Removing previously pending release, as it was grabbed.");
                    Delete(existingReport);
                }
            }
        }
예제 #11
0
        public override string Download(RemoteAlbum remoteAlbum)
        {
            var url   = remoteAlbum.Release.DownloadUrl;
            var title = remoteAlbum.Release.Title;

            if (remoteAlbum.ParsedAlbumInfo.Discography)
            {
                throw new NotSupportedException("Discography releases are not supported with Pneumatic.");
            }

            title = FileNameBuilder.CleanFileName(title);

            //Save to the Pneumatic directory (The user will need to ensure its accessible by XBMC)
            var nzbFile = Path.Combine(Settings.NzbFolder, title + ".nzb");

            _logger.Debug("Downloading NZB from: {0} to: {1}", url, nzbFile);
            _httpClient.DownloadFile(url, nzbFile);

            _logger.Debug("NZB Download succeeded, saved to: {0}", nzbFile);

            var strmFile = WriteStrmFile(title, nzbFile);


            return(GetDownloadClientId(strmFile));
        }
예제 #12
0
        public virtual Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
        {
            var qualityProfile = subject.Artist.QualityProfile.Value;

            foreach (var album in subject.Albums)
            {
                var tracksMissing = _missingFilesCache.Get(album.Id.ToString(), () => _trackService.TracksWithoutFiles(album.Id).Any(),
                                                           TimeSpan.FromSeconds(30));
                var trackFiles = _mediaFileService.GetFilesByAlbum(album.Id);

                if (!tracksMissing && trackFiles.Any())
                {
                    // Get a distinct list of all current track qualities for a given album
                    var currentQualities = trackFiles.Select(c => c.Quality).Distinct().ToList();

                    _logger.Debug("Comparing file quality with report. Existing files contain {0}", currentQualities.ConcatToString());

                    if (!_upgradableSpecification.CutoffNotMet(qualityProfile,
                                                               currentQualities,
                                                               _preferredWordServiceCalculator.Calculate(subject.Artist, trackFiles[0].GetSceneOrFileName()),
                                                               subject.ParsedAlbumInfo.Quality,
                                                               subject.PreferredWordScore))
                    {
                        _logger.Debug("Cutoff already met by existing files, rejecting.");

                        var qualityCutoffIndex = qualityProfile.GetIndex(qualityProfile.Cutoff);
                        var qualityCutoff      = qualityProfile.Items[qualityCutoffIndex.Index];

                        return(Decision.Reject("Existing files meets cutoff: {0}", qualityCutoff));
                    }
                }
            }

            return(Decision.Accept());
        }
예제 #13
0
        protected override string AddFromMagnetLink(RemoteAlbum remoteAlbum, string hash, string magnetLink)
        {
            var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings);

            if (actualHash.IsNullOrWhiteSpace())
            {
                throw new DownloadClientException("Deluge failed to add magnet " + magnetLink);
            }

            if (!Settings.MusicCategory.IsNullOrWhiteSpace())
            {
                _proxy.SetLabel(actualHash, Settings.MusicCategory, Settings);
            }

            _proxy.SetTorrentSeedingConfiguration(actualHash, remoteAlbum.SeedConfiguration, Settings);

            var isRecentAlbum = remoteAlbum.IsRecentAlbum();

            if (isRecentAlbum && Settings.RecentTvPriority == (int)DelugePriority.First ||
                !isRecentAlbum && Settings.OlderTvPriority == (int)DelugePriority.First)
            {
                _proxy.MoveTorrentToTopInQueue(actualHash, Settings);
            }

            return(actualHash.ToUpper());
        }
예제 #14
0
        public virtual Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
        {
            foreach (var album in subject.Albums)
            {
                var tracksMissing = _missingFilesCache.Get(album.Id.ToString(),
                                                           () => _trackService.TracksWithoutFiles(album.Id).Any(),
                                                           TimeSpan.FromSeconds(30));
                var trackFiles = _mediaFileService.GetFilesByAlbum(album.Id);

                if (!tracksMissing && trackFiles.Any())
                {
                    var currentQualities = trackFiles.Select(c => c.Quality).Distinct().ToList();

                    if (!_upgradableSpecification.IsUpgradable(subject.Artist.QualityProfile,
                                                               currentQualities,
                                                               _preferredWordServiceCalculator.Calculate(subject.Artist, trackFiles[0].GetSceneOrFileName(), subject.Release?.IndexerId ?? 0),
                                                               subject.ParsedAlbumInfo.Quality,
                                                               subject.PreferredWordScore))
                    {
                        return(Decision.Reject("Existing files on disk is of equal or higher preference: {0}", currentQualities.ConcatToString()));
                    }
                }
            }

            return(Decision.Accept());
        }
예제 #15
0
        public virtual Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
        {
            if (!_configService.AutoUnmonitorPreviouslyDownloadedTracks)
            {
                return(Decision.Accept());
            }

            if (searchCriteria != null)
            {
                _logger.Debug("Skipping deleted trackfile check during search");
                return(Decision.Accept());
            }

            var missingTrackFiles = subject.Albums
                                    .SelectMany(v => _albumService.GetFilesByAlbum(v.Id))
                                    .DistinctBy(v => v.Id)
                                    .Where(v => IsTrackFileMissing(subject.Artist, v))
                                    .ToArray();


            if (missingTrackFiles.Any())
            {
                foreach (var missingTrackFile in missingTrackFiles)
                {
                    _logger.Trace("Track file {0} is missing from disk.", missingTrackFile.Path);
                }

                _logger.Debug("Files for this album exist in the database but not on disk, will be unmonitored on next diskscan. skipping.");
                return(Decision.Reject("Artist is not monitored"));
            }

            return(Decision.Accept());
        }
예제 #16
0
        public Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
        {
            var size        = subject.Release.Size;
            var maximumSize = _configService.MaximumSize.Megabytes();

            if (maximumSize == 0)
            {
                _logger.Debug("Maximum size is not set.");
                return(Decision.Accept());
            }

            if (subject.Release.Size == 0)
            {
                _logger.Debug("Release has unknown size, skipping size check.");
                return(Decision.Accept());
            }

            _logger.Debug("Checking if release meets maximum size requirements. {0}", size.SizeSuffix());

            if (size > maximumSize)
            {
                var message = $"{size.SizeSuffix()} is too big, maximum size is {maximumSize.SizeSuffix()}";

                _logger.Debug(message);
                return(Decision.Reject(message));
            }

            return(Decision.Accept());
        }
예제 #17
0
        public void Setup()
        {
            _monitoredAlbumSpecification = Mocker.Resolve <MonitoredAlbumSpecification>();

            _fakeArtist = Builder <Artist> .CreateNew()
                          .With(c => c.Monitored = true)
                          .Build();

            _firstAlbum = new Album {
                Monitored = true
            };
            _secondAlbum = new Album {
                Monitored = true
            };

            var singleAlbumList = new List <Album> {
                _firstAlbum
            };
            var doubleAlbumList = new List <Album> {
                _firstAlbum, _secondAlbum
            };

            _parseResultMulti = new RemoteAlbum
            {
                Artist = _fakeArtist,
                Albums = doubleAlbumList
            };

            _parseResultSingle = new RemoteAlbum
            {
                Artist = _fakeArtist,
                Albums = singleAlbumList
            };
        }
예제 #18
0
        protected override string AddFromTorrentFile(RemoteAlbum remoteAlbum, string hash, string filename, byte[] fileContent)
        {
            var actualHash = _proxy.AddTorrentFromFile(filename, fileContent, Settings);

            if (actualHash.IsNullOrWhiteSpace())
            {
                throw new DownloadClientException("Deluge failed to add torrent " + filename);
            }

            _proxy.SetTorrentSeedingConfiguration(actualHash, remoteAlbum.SeedConfiguration, Settings);

            if (Settings.MusicCategory.IsNotNullOrWhiteSpace())
            {
                _proxy.SetTorrentLabel(actualHash, Settings.MusicCategory, Settings);
            }

            var isRecentAlbum = remoteAlbum.IsRecentAlbum();

            if ((isRecentAlbum && Settings.RecentTvPriority == (int)DelugePriority.First) ||
                (!isRecentAlbum && Settings.OlderTvPriority == (int)DelugePriority.First))
            {
                _proxy.MoveTorrentToTopInQueue(actualHash, Settings);
            }

            return(actualHash.ToUpper());
        }
예제 #19
0
        protected override string AddFromMagnetLink(RemoteAlbum remoteAlbum, string hash, string magnetLink)
        {
            if (!Proxy.GetConfig(Settings).DhtEnabled&& !magnetLink.Contains("&tr="))
            {
                throw new NotSupportedException("Magnet Links without trackers not supported if DHT is disabled");
            }

            Proxy.AddTorrentFromUrl(magnetLink, Settings);

            if (Settings.MusicCategory.IsNotNullOrWhiteSpace())
            {
                Proxy.SetTorrentLabel(hash.ToLower(), Settings.MusicCategory, Settings);
            }

            var isRecentAlbum = remoteAlbum.IsRecentAlbum();

            if (isRecentAlbum && Settings.RecentTvPriority == (int)QBittorrentPriority.First ||
                !isRecentAlbum && Settings.OlderTvPriority == (int)QBittorrentPriority.First)
            {
                Proxy.MoveTorrentToTopInQueue(hash.ToLower(), Settings);
            }

            SetInitialState(hash.ToLower());

            if (remoteAlbum.SeedConfiguration != null && (remoteAlbum.SeedConfiguration.Ratio.HasValue || remoteAlbum.SeedConfiguration.SeedTime.HasValue))
            {
                Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteAlbum.SeedConfiguration, Settings);
            }

            return(hash);
        }
예제 #20
0
        private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteAlbum remoteAlbum, SearchCriteriaBase searchCriteriaBase = null)
        {
            try
            {
                var result = spec.IsSatisfiedBy(remoteAlbum, searchCriteriaBase);

                if (!result.Accepted)
                {
                    return(new Rejection(result.Reason, spec.Type));
                }
            }
            catch (NotImplementedException)
            {
                _logger.Trace("Spec " + spec.GetType().Name + " not implemented.");
            }
            catch (Exception e)
            {
                e.Data.Add("report", remoteAlbum.Release.ToJson());
                e.Data.Add("parsed", remoteAlbum.ParsedAlbumInfo.ToJson());
                _logger.Error(e, "Couldn't evaluate decision on {0}", remoteAlbum.Release.Title);
                return(new Rejection($"{spec.GetType().Name}: {e.Message}"));
            }

            return(null);
        }
        public void Setup()
        {
            _artist = Builder <Artist> .CreateNew().With(s => s.Id = 1).Build();

            _album1 = Builder <Album> .CreateNew().With(s => s.ReleaseDate = DateTime.Today).Build();

            _album2 = Builder <Album> .CreateNew().With(s => s.ReleaseDate = DateTime.Today).Build();

            _remoteAlbum = new RemoteAlbum
            {
                Artist = _artist,
                Albums = new List <Album> {
                    _album1
                },
                Release = new TorrentInfo
                {
                    IndexerId   = 1,
                    Title       = "Artist - Album [FLAC-RlsGrp]",
                    PublishDate = DateTime.Today
                }
            };

            _indexerDefinition = new IndexerDefinition
            {
                Settings = new TorrentRssIndexerSettings {
                    EarlyReleaseLimit = 5
                }
            };

            Mocker.GetMock <IIndexerFactory>()
            .Setup(v => v.Get(1))
            .Returns(_indexerDefinition);
        }
예제 #22
0
 public void Setup()
 {
     _remoteAlbum = new RemoteAlbum()
     {
         Release = new ReleaseInfo()
     };
 }
        public void Setup()
        {
            Mocker.Resolve <UpgradableSpecification>();

            _firstFile = new TrackFile {
                Quality = new QualityModel(Quality.FLAC, new Revision(version: 2)), DateAdded = DateTime.Now
            };
            _secondFile = new TrackFile {
                Quality = new QualityModel(Quality.FLAC, new Revision(version: 2)), DateAdded = DateTime.Now
            };

            var singleAlbumList = new List <Album> {
                new Album {
                }
            };
            var doubleAlbumList = new List <Album> {
                new Album {
                }, new Album {
                }, new Album {
                }
            };

            var fakeArtist = Builder <Artist> .CreateNew()
                             .With(c => c.QualityProfile = new QualityProfile
            {
                UpgradeAllowed = true,
                Cutoff         = Quality.MP3_320.Id,
                Items          = Qualities.QualityFixture.GetDefaultQualities()
            })
                             .Build();

            Mocker.GetMock <ITrackService>()
            .Setup(c => c.TracksWithoutFiles(It.IsAny <int>()))
            .Returns(new List <Track>());

            Mocker.GetMock <IMediaFileService>()
            .Setup(c => c.GetFilesByAlbum(It.IsAny <int>()))
            .Returns(new List <TrackFile> {
                _firstFile, _secondFile
            });

            _parseResultMulti = new RemoteAlbum
            {
                Artist          = fakeArtist,
                ParsedAlbumInfo = new ParsedAlbumInfo {
                    Quality = new QualityModel(Quality.MP3_256, new Revision(version: 2))
                },
                Albums = doubleAlbumList
            };

            _parseResultSingle = new RemoteAlbum
            {
                Artist          = fakeArtist,
                ParsedAlbumInfo = new ParsedAlbumInfo {
                    Quality = new QualityModel(Quality.MP3_256, new Revision(version: 2))
                },
                Albums = singleAlbumList
            };
        }
예제 #24
0
        private int GetDelay(RemoteAlbum remoteAlbum)
        {
            var delayProfile = _delayProfileService.AllForTags(remoteAlbum.Artist.Tags).OrderBy(d => d.Order).First();
            var delay        = delayProfile.GetProtocolDelay(remoteAlbum.Release.DownloadProtocol);
            var minimumAge   = _configService.MinimumAge;

            return(new[] { delay, minimumAge }.Max());
        }
예제 #25
0
        public Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
        {
            var queue         = _queueService.GetQueue();
            var matchingAlbum = queue.Where(q => q.RemoteAlbum != null &&
                                            q.RemoteAlbum.Artist != null &&
                                            q.RemoteAlbum.Artist.Id == subject.Artist.Id &&
                                            q.RemoteAlbum.Albums.Select(e => e.Id).Intersect(subject.Albums.Select(e => e.Id)).Any())
                                .ToList();


            foreach (var queueItem in matchingAlbum)
            {
                var remoteAlbum    = queueItem.RemoteAlbum;
                var qualityProfile = subject.Artist.QualityProfile.Value;

                _logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteAlbum.ParsedAlbumInfo.Quality);
                var queuedItemPreferredWordScore = _preferredWordServiceCalculator.Calculate(subject.Artist, queueItem.Title);

                if (!_upgradableSpecification.CutoffNotMet(qualityProfile,
                                                           new List <QualityModel> {
                    remoteAlbum.ParsedAlbumInfo.Quality
                },
                                                           queuedItemPreferredWordScore,
                                                           subject.ParsedAlbumInfo.Quality,
                                                           subject.PreferredWordScore))

                {
                    return(Decision.Reject("Release in queue already meets cutoff: {0}", remoteAlbum.ParsedAlbumInfo.Quality));
                }

                _logger.Debug("Checking if release is higher quality than queued release. Queued: {0}", remoteAlbum.ParsedAlbumInfo.Quality);

                if (!_upgradableSpecification.IsUpgradable(qualityProfile,
                                                           new List <QualityModel> {
                    remoteAlbum.ParsedAlbumInfo.Quality
                },
                                                           queuedItemPreferredWordScore,
                                                           subject.ParsedAlbumInfo.Quality,
                                                           subject.PreferredWordScore))
                {
                    return(Decision.Reject("Release in queue is of equal or higher preference: {0}", remoteAlbum.ParsedAlbumInfo.Quality));
                }

                _logger.Debug("Checking if profiles allow upgrading. Queued: {0}", remoteAlbum.ParsedAlbumInfo.Quality);

                if (!_upgradableSpecification.IsUpgradeAllowed(qualityProfile,
                                                               new List <QualityModel> {
                    remoteAlbum.ParsedAlbumInfo.Quality
                },
                                                               subject.ParsedAlbumInfo.Quality))
                {
                    return(Decision.Reject("Another release is queued and the Quality profile does not allow upgrades"));
                }
            }

            return(Decision.Accept());
        }
        public void should_unmap_tracked_download_if_album_deleted()
        {
            GivenDownloadHistory();

            var remoteAlbum = new RemoteAlbum
            {
                Artist = new Artist()
                {
                    Id = 5
                },
                Albums = new List <Album> {
                    new Album {
                        Id = 4
                    }
                },
                ParsedAlbumInfo = new ParsedAlbumInfo()
                {
                    AlbumTitle = "Audio Album",
                    ArtistName = "Audio Artist"
                }
            };

            Mocker.GetMock <IParsingService>()
            .Setup(s => s.Map(It.Is <ParsedAlbumInfo>(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny <int>(), It.IsAny <IEnumerable <int> >()))
            .Returns(remoteAlbum);

            var client = new DownloadClientDefinition()
            {
                Id       = 1,
                Protocol = DownloadProtocol.Torrent
            };

            var item = new DownloadClientItem()
            {
                Title      = "Audio Artist - Audio Album [2018 - FLAC]",
                DownloadId = "35238",
            };

            // get a tracked download in place
            var trackedDownload = Subject.TrackDownload(client, item);

            Subject.GetTrackedDownloads().Should().HaveCount(1);

            // simulate deletion - album no longer maps
            Mocker.GetMock <IParsingService>()
            .Setup(s => s.Map(It.Is <ParsedAlbumInfo>(i => i.AlbumTitle == "Audio Album" && i.ArtistName == "Audio Artist"), It.IsAny <int>(), It.IsAny <IEnumerable <int> >()))
            .Returns(default(RemoteAlbum));

            // handle deletion event
            Subject.Handle(new AlbumDeletedEvent(remoteAlbum.Albums.First(), false));

            // verify download has null remote album
            var trackedDownloads = Subject.GetTrackedDownloads();

            trackedDownloads.Should().HaveCount(1);
            trackedDownloads.First().RemoteAlbum.Should().BeNull();
        }
        public void Setup()
        {
            Mocker.Resolve <UpgradableSpecification>();
            _upgradeHistory = Mocker.Resolve <HistorySpecification>();

            var singleAlbumList = new List <Album> {
                new Album {
                    Id = FIRST_ALBUM_ID
                }
            };
            var doubleAlbumList = new List <Album>
            {
                new Album {
                    Id = FIRST_ALBUM_ID
                },
                new Album {
                    Id = SECOND_ALBUM_ID
                },
                new Album {
                    Id = 3
                }
            };

            _fakeArtist = Builder <Artist> .CreateNew()
                          .With(c => c.QualityProfile = new QualityProfile
            {
                UpgradeAllowed = true,
                Cutoff         = Quality.MP3_320.Id,
                Items          = Qualities.QualityFixture.GetDefaultQualities()
            })
                          .Build();

            _parseResultMulti = new RemoteAlbum
            {
                Artist          = _fakeArtist,
                ParsedAlbumInfo = new ParsedAlbumInfo {
                    Quality = new QualityModel(Quality.MP3_192, new Revision(version: 2))
                },
                Albums = doubleAlbumList
            };

            _parseResultSingle = new RemoteAlbum
            {
                Artist          = _fakeArtist,
                ParsedAlbumInfo = new ParsedAlbumInfo {
                    Quality = new QualityModel(Quality.MP3_192, new Revision(version: 2))
                },
                Albums = singleAlbumList
            };

            _upgradableQuality    = new QualityModel(Quality.MP3_192, new Revision(version: 1));
            _notupgradableQuality = new QualityModel(Quality.MP3_320, new Revision(version: 2));

            Mocker.GetMock <IConfigService>()
            .SetupGet(s => s.EnableCompletedDownloadHandling)
            .Returns(true);
        }
예제 #28
0
 public WebhookRelease(QualityModel quality, RemoteAlbum remoteAlbum)
 {
     Quality        = quality.Quality.Name;
     QualityVersion = quality.Revision.Version;
     ReleaseGroup   = remoteAlbum.ParsedAlbumInfo.ReleaseGroup;
     ReleaseTitle   = remoteAlbum.Release.Title;
     Indexer        = remoteAlbum.Release.Indexer;
     Size           = remoteAlbum.Release.Size;
 }
예제 #29
0
        public Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
        {
            _logger.Debug("Beginning size check for: {0}", subject);

            var quality = subject.ParsedAlbumInfo.Quality.Quality;

            if (subject.Release.Size == 0)
            {
                _logger.Debug("Release has unknown size, skipping size check.");
                return(Decision.Accept());
            }

            var qualityDefinition = _qualityDefinitionService.Get(quality);

            if (qualityDefinition.MinSize.HasValue)
            {
                var minSize            = qualityDefinition.MinSize.Value.Kilobits();
                var minReleaseDuration = subject.Albums.Select(a => a.AlbumReleases.Value.Where(r => r.Monitored || a.AnyReleaseOk).Select(r => r.Duration).Min()).Sum() / 1000;

                //Multiply minSize by smallest release duration
                minSize = minSize * minReleaseDuration;

                //If the parsed size is smaller than minSize we don't want it
                if (subject.Release.Size < minSize)
                {
                    var runtimeMessage = $"{minReleaseDuration}sec";

                    _logger.Debug("Item: {0}, Size: {1} is smaller than minimum allowed size ({2} bytes for {3}), rejecting.", subject, subject.Release.Size, minSize, runtimeMessage);
                    return(Decision.Reject("{0} is smaller than minimum allowed {1}", subject.Release.Size.SizeSuffix(), minSize.SizeSuffix()));
                }
            }
            if (!qualityDefinition.MaxSize.HasValue || qualityDefinition.MaxSize.Value == 0)
            {
                _logger.Debug("Max size is unlimited - skipping check.");
            }
            else
            {
                var maxSize            = qualityDefinition.MaxSize.Value.Kilobits();
                var maxReleaseDuration = subject.Albums.Select(a => a.AlbumReleases.Value.Where(r => r.Monitored || a.AnyReleaseOk).Select(r => r.Duration).Max()).Sum() / 1000;

                //Multiply maxSize by Album.Duration
                maxSize = maxSize * maxReleaseDuration;

                //If the parsed size is greater than maxSize we don't want it
                if (subject.Release.Size > maxSize)
                {
                    var runtimeMessage = $"{maxReleaseDuration}sec";

                    _logger.Debug("Item: {0}, Size: {1} is greater than maximum allowed size ({2} bytes for {3}), rejecting.", subject, subject.Release.Size, maxSize, runtimeMessage);
                    return(Decision.Reject("{0} is larger than maximum allowed {1}", subject.Release.Size.SizeSuffix(), maxSize.SizeSuffix()));
                }
            }

            _logger.Debug("Item: {0}, meets size constraints.", subject);
            return(Decision.Accept());
        }
예제 #30
0
        protected override string AddFromTorrentFile(RemoteAlbum remoteAlbum, string hash, string filename, byte[] fileContent)
        {
            var setShareLimits       = remoteAlbum.SeedConfiguration != null && (remoteAlbum.SeedConfiguration.Ratio.HasValue || remoteAlbum.SeedConfiguration.SeedTime.HasValue);
            var addHasSetShareLimits = setShareLimits && ProxyApiVersion >= new Version(2, 8, 1);
            var isRecentAlbum        = remoteAlbum.IsRecentAlbum();
            var moveToTop            = (isRecentAlbum && Settings.RecentTvPriority == (int)QBittorrentPriority.First) || (!isRecentAlbum && Settings.OlderTvPriority == (int)QBittorrentPriority.First);
            var forceStart           = (QBittorrentState)Settings.InitialState == QBittorrentState.ForceStart;

            Proxy.AddTorrentFromFile(filename, fileContent, addHasSetShareLimits ? remoteAlbum.SeedConfiguration : null, Settings);

            if ((!addHasSetShareLimits && setShareLimits) || moveToTop || forceStart)
            {
                if (!WaitForTorrent(hash))
                {
                    return(hash);
                }

                if (!addHasSetShareLimits && setShareLimits)
                {
                    try
                    {
                        Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteAlbum.SeedConfiguration, Settings);
                    }
                    catch (Exception ex)
                    {
                        _logger.Warn(ex, "Failed to set the torrent seed criteria for {0}.", hash);
                    }
                }

                if (moveToTop)
                {
                    try
                    {
                        Proxy.MoveTorrentToTopInQueue(hash.ToLower(), Settings);
                    }
                    catch (Exception ex)
                    {
                        _logger.Warn(ex, "Failed to set the torrent priority for {0}.", hash);
                    }
                }

                if (forceStart)
                {
                    try
                    {
                        Proxy.SetForceStart(hash.ToLower(), true, Settings);
                    }
                    catch (Exception ex)
                    {
                        _logger.Warn(ex, "Failed to set ForceStart for {0}.", hash);
                    }
                }
            }

            return(hash);
        }