New: Readarr 0.1

This commit is contained in:
ta264
2020-05-06 21:14:11 +01:00
parent 476f2d6047
commit 08496c82af
911 changed files with 14837 additions and 24442 deletions
@@ -70,29 +70,23 @@ namespace NzbDrone.Core.DecisionEngine
{
var parsedAlbumInfo = Parser.Parser.ParseAlbumTitle(report.Title);
if (parsedAlbumInfo == null && searchCriteria != null)
if (parsedAlbumInfo == null)
{
parsedAlbumInfo = Parser.Parser.ParseAlbumTitleWithSearchCriteria(report.Title,
searchCriteria.Artist,
searchCriteria.Albums);
if (searchCriteria != null)
{
parsedAlbumInfo = Parser.Parser.ParseAlbumTitleWithSearchCriteria(report.Title,
searchCriteria.Artist,
searchCriteria.Albums);
}
else
{
// try parsing fuzzy
parsedAlbumInfo = _parsingService.ParseAlbumTitleFuzzy(report.Title);
}
}
if (parsedAlbumInfo != null)
{
// TODO: Artist Data Augment without calling to parse title again
//if (!report.Artist.IsNullOrWhiteSpace())
//{
// if (parsedAlbumInfo.ArtistName.IsNullOrWhiteSpace() || _parsingService.GetArtist(parsedAlbumInfo.ArtistName) == null)
// {
// parsedAlbumInfo.ArtistName = report.Artist;
// }
//}
// TODO: Replace Parsed AlbumTitle with metadata Title if Parsed AlbumTitle not a valid match
//if (!report.Album.IsNullOrWhiteSpace())
//{
// parsedAlbumInfo.AlbumTitle = report.Album;
//}
if (!parsedAlbumInfo.ArtistName.IsNullOrWhiteSpace())
{
var remoteAlbum = _parsingService.Map(parsedAlbumInfo, searchCriteria);
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.DecisionEngine
public List<DownloadDecision> PrioritizeDecisions(List<DownloadDecision> decisions)
{
return decisions.Where(c => c.RemoteAlbum.DownloadAllowed)
.GroupBy(c => c.RemoteAlbum.Artist.Id, (artistId, downloadDecisions) =>
.GroupBy(c => c.RemoteAlbum.Artist.Id, (authorId, downloadDecisions) =>
{
return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_configService, _delayProfileService));
})
@@ -23,6 +23,10 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
{
_logger.Debug("size restriction not implemented");
return Decision.Accept();
/*
_logger.Debug("Beginning size check for: {0}", subject);
var quality = subject.ParsedAlbumInfo.Quality.Quality;
@@ -38,17 +42,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
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);
_logger.Debug("Item: {0}, Size: {1} is smaller than minimum allowed size ({2} bytes), rejecting.", subject, subject.Release.Size, minSize);
return Decision.Reject("{0} is smaller than minimum allowed {1}", subject.Release.Size.SizeSuffix(), minSize.SizeSuffix());
}
}
@@ -60,23 +58,18 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
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);
_logger.Debug("Item: {0}, Size: {1} is greater than maximum allowed size ({2} bytes), rejecting.", subject, subject.Release.Size, maxSize);
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();
*/
}
}
}
@@ -1,36 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Music;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles.Releases;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class CutoffSpecification : IDecisionEngineSpecification
{
private readonly UpgradableSpecification _upgradableSpecification;
private readonly IMediaFileService _mediaFileService;
private readonly ITrackService _trackService;
private readonly Logger _logger;
private readonly ICached<bool> _missingFilesCache;
private readonly IPreferredWordService _preferredWordServiceCalculator;
public CutoffSpecification(UpgradableSpecification upgradableSpecification,
Logger logger,
ICacheManager cacheManager,
IMediaFileService mediaFileService,
IPreferredWordService preferredWordServiceCalculator,
ITrackService trackService)
Logger logger)
{
_upgradableSpecification = upgradableSpecification;
_mediaFileService = mediaFileService;
_trackService = trackService;
_missingFilesCache = cacheManager.GetCache<bool>(GetType());
_preferredWordServiceCalculator = preferredWordServiceCalculator;
_logger = logger;
}
@@ -42,33 +31,25 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{
var qualityProfile = subject.Artist.QualityProfile.Value;
foreach (var album in subject.Albums)
foreach (var file in subject.Albums.SelectMany(b => b.BookFiles.Value))
{
var tracksMissing = _missingFilesCache.Get(album.Id.ToString(),
() => _trackService.TracksWithoutFiles(album.Id).Any(),
TimeSpan.FromSeconds(30));
var trackFiles = _mediaFileService.GetFilesByAlbum(album.Id);
// Get a distinct list of all current track qualities for a given album
var currentQualities = new List<QualityModel> { file.Quality };
if (!tracksMissing && trackFiles.Any())
_logger.Debug("Comparing file quality with report. Existing files contain {0}", currentQualities.ConcatToString());
if (!_upgradableSpecification.CutoffNotMet(qualityProfile,
currentQualities,
_preferredWordServiceCalculator.Calculate(subject.Artist, file.GetSceneOrFileName()),
subject.ParsedAlbumInfo.Quality,
subject.PreferredWordScore))
{
// Get a distinct list of all current track qualities for a given album
var currentQualities = trackFiles.Select(c => c.Quality).Distinct().ToList();
_logger.Debug("Cutoff already met by existing files, rejecting.");
_logger.Debug("Comparing file quality with report. Existing files contain {0}", currentQualities.ConcatToString());
var qualityCutoffIndex = qualityProfile.GetIndex(qualityProfile.Cutoff);
var qualityCutoff = qualityProfile.Items[qualityCutoffIndex.Index];
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.Reject("Existing files meets cutoff: {0}", qualityCutoff);
}
}
@@ -92,9 +92,9 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
return Decision.Accept();
}
var albumIds = subject.Albums.Select(e => e.Id);
var bookIds = subject.Albums.Select(e => e.Id);
var oldest = _pendingReleaseService.OldestPendingRelease(subject.Artist.Id, albumIds.ToArray());
var oldest = _pendingReleaseService.OldestPendingRelease(subject.Artist.Id, bookIds.ToArray());
if (oldest != null && oldest.Release.AgeMinutes > delay)
{
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
return Decision.Accept();
}
private bool IsTrackFileMissing(Artist artist, TrackFile trackFile)
private bool IsTrackFileMissing(Author artist, BookFile trackFile)
{
return !_diskProvider.FileExists(trackFile.Path);
}
@@ -1,29 +0,0 @@
using System;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class SameTracksGrabSpecification : IDecisionEngineSpecification
{
private readonly SameTracksSpecification _sameTracksSpecification;
private readonly Logger _logger;
public SameTracksGrabSpecification(SameTracksSpecification sameTracksSpecification, Logger logger)
{
_sameTracksSpecification = sameTracksSpecification;
_logger = logger;
}
public SpecificationPriority Priority => SpecificationPriority.Default;
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
// TODO: Rework for Tracks if we can parse from release details.
}
}
}
@@ -1,35 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Music;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class SameTracksSpecification
{
private readonly ITrackService _trackService;
public SameTracksSpecification(ITrackService trackService)
{
_trackService = trackService;
}
public bool IsSatisfiedBy(List<Track> tracks)
{
var trackIds = tracks.SelectList(e => e.Id);
var trackFileIds = tracks.Where(c => c.TrackFileId != 0).Select(c => c.TrackFileId).Distinct();
foreach (var trackFileId in trackFileIds)
{
var tracksInFile = _trackService.GetTracksByFileId(trackFileId);
if (tracksInFile.Select(e => e.Id).Except(trackIds).Any())
{
return false;
}
}
return true;
}
}
}
@@ -1,33 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Music;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class UpgradeAllowedSpecification : IDecisionEngineSpecification
{
private readonly UpgradableSpecification _upgradableSpecification;
private readonly IMediaFileService _mediaFileService;
private readonly ITrackService _trackService;
private readonly Logger _logger;
private readonly ICached<bool> _missingFilesCache;
public UpgradeAllowedSpecification(UpgradableSpecification upgradableSpecification,
Logger logger,
ICacheManager cacheManager,
IMediaFileService mediaFileService,
ITrackService trackService)
Logger logger)
{
_upgradableSpecification = upgradableSpecification;
_mediaFileService = mediaFileService;
_trackService = trackService;
_missingFilesCache = cacheManager.GetCache<bool>(GetType());
_logger = logger;
}
@@ -38,29 +27,26 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{
var qualityProfile = subject.Artist.QualityProfile.Value;
foreach (var album in subject.Albums)
foreach (var file in subject.Albums.SelectMany(b => b.BookFiles.Value))
{
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())
if (file == null)
{
// Get a distinct list of all current track qualities for a given album
var currentQualities = trackFiles.Select(c => c.Quality).Distinct().ToList();
_logger.Debug("File is no longer available, skipping this file.");
continue;
}
_logger.Debug("Comparing file quality with report. Existing files contain {0}", currentQualities.ConcatToString());
// Get a distinct list of all current track qualities for a given album
var currentQualities = new List<QualityModel> { file.Quality };
if (!_upgradableSpecification.IsUpgradeAllowed(qualityProfile,
_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");
{
_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.Reject("Existing files and the Quality profile does not allow upgrades");
}
}
@@ -1,38 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Music;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles.Releases;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class UpgradeDiskSpecification : IDecisionEngineSpecification
{
private readonly IMediaFileService _mediaFileService;
private readonly ITrackService _trackService;
private readonly UpgradableSpecification _upgradableSpecification;
private readonly IPreferredWordService _preferredWordServiceCalculator;
private readonly Logger _logger;
private readonly ICached<bool> _missingFilesCache;
public UpgradeDiskSpecification(UpgradableSpecification qualityUpgradableSpecification,
IMediaFileService mediaFileService,
ITrackService trackService,
ICacheManager cacheManager,
IPreferredWordService preferredWordServiceCalculator,
Logger logger)
{
_upgradableSpecification = qualityUpgradableSpecification;
_mediaFileService = mediaFileService;
_trackService = trackService;
_preferredWordServiceCalculator = preferredWordServiceCalculator;
_logger = logger;
_missingFilesCache = cacheManager.GetCache<bool>(GetType());
}
public SpecificationPriority Priority => SpecificationPriority.Default;
@@ -40,25 +28,23 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public virtual Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
{
foreach (var album in subject.Albums)
foreach (var file in subject.Albums.SelectMany(c => c.BookFiles.Value))
{
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())
if (file == null)
{
var currentQualities = trackFiles.Select(c => c.Quality).Distinct().ToList();
_logger.Debug("File is no longer available, skipping this file.");
continue;
}
if (!_upgradableSpecification.IsUpgradable(subject.Artist.QualityProfile,
currentQualities,
_preferredWordServiceCalculator.Calculate(subject.Artist, trackFiles[0].GetSceneOrFileName()),
subject.ParsedAlbumInfo.Quality,
subject.PreferredWordScore))
{
return Decision.Reject("Existing files on disk is of equal or higher preference: {0}", currentQualities.ConcatToString());
}
_logger.Debug("Comparing file quality and language with report. Existing file is {0}", file.Quality);
if (!_upgradableSpecification.IsUpgradable(subject.Artist.QualityProfile,
new List<QualityModel> { file.Quality },
_preferredWordServiceCalculator.Calculate(subject.Artist, file.GetSceneOrFileName()),
subject.ParsedAlbumInfo.Quality,
subject.PreferredWordScore))
{
return Decision.Reject("Existing file on disk is of equal or higher preference: {0}", file.Quality);
}
}