diff --git a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs index ddd41f435..3bf22b187 100644 --- a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs @@ -22,6 +22,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent { private readonly IRTorrentProxy _proxy; private readonly IRTorrentDirectoryValidator _rTorrentDirectoryValidator; + private readonly IDownloadSeedConfigProvider _downloadSeedConfigProvider; public RTorrent(IRTorrentProxy proxy, ITorrentFileInfoReader torrentFileInfoReader, @@ -29,12 +30,14 @@ namespace NzbDrone.Core.Download.Clients.RTorrent IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, + IDownloadSeedConfigProvider downloadSeedConfigProvider, IRTorrentDirectoryValidator rTorrentDirectoryValidator, Logger logger) : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) { _proxy = proxy; _rTorrentDirectoryValidator = rTorrentDirectoryValidator; + _downloadSeedConfigProvider = downloadSeedConfigProvider; } public override void MarkItemAsImported(DownloadClientItem downloadClientItem) @@ -53,6 +56,8 @@ namespace NzbDrone.Core.Download.Clients.RTorrent Settings.TvImportedCategory, downloadClientItem.Title); } } + + // TODO: Set post-import view } protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) @@ -147,6 +152,9 @@ namespace NzbDrone.Core.Download.Clients.RTorrent item.Status = DownloadItemStatus.Paused; } + var seedConfig = _downloadSeedConfigProvider.GetSeedConfiguration(torrent.Hash); + // TODO: Handle + // No stop ratio data is present, so do not delete item.CanMoveFiles = item.CanBeRemoved = false; diff --git a/src/NzbDrone.Core/Download/DownloadSeedConfigProvider.cs b/src/NzbDrone.Core/Download/DownloadSeedConfigProvider.cs new file mode 100644 index 000000000..f7d0ee038 --- /dev/null +++ b/src/NzbDrone.Core/Download/DownloadSeedConfigProvider.cs @@ -0,0 +1,79 @@ +using System; +using NLog; +using NzbDrone.Common.Cache; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Download.Clients; +using NzbDrone.Core.Download.History; +using NzbDrone.Core.Indexers; + +namespace NzbDrone.Core.Download +{ + public interface IDownloadSeedConfigProvider + { + TorrentSeedConfiguration GetSeedConfiguration(string infoHash); + } + + public class DownloadSeedConfigProvider : IDownloadSeedConfigProvider + { + private readonly Logger _logger; + private readonly ISeedConfigProvider _indexerSeedConfigProvider; + private readonly IDownloadHistoryService _downloadHistoryService; + + class CachedSeedConfiguration + { + public int IndexerId { get; set; } + public bool FullSeason { get; set; } + } + + private readonly ICached _cacheDownloads; + + public DownloadSeedConfigProvider(IDownloadHistoryService downloadHistoryService, ISeedConfigProvider indexerSeedConfigProvider, ICacheManager cacheManager, Logger logger) + { + _logger = logger; + _indexerSeedConfigProvider = indexerSeedConfigProvider; + _downloadHistoryService = downloadHistoryService; + + _cacheDownloads = cacheManager.GetRollingCache(GetType(), "indexerByHash", TimeSpan.FromHours(1)); + } + + public TorrentSeedConfiguration GetSeedConfiguration(string infoHash) + { + if (infoHash.IsNullOrWhiteSpace()) return null; + + infoHash = infoHash.ToUpper(); + + var cachedConfig = _cacheDownloads.Get(infoHash, () => FetchIndexer(infoHash)); + + if (cachedConfig == null) return null; + + var seedConfig = _indexerSeedConfigProvider.GetSeedConfiguration(cachedConfig.IndexerId, cachedConfig.FullSeason); + + return seedConfig; + } + + private CachedSeedConfiguration FetchIndexer(string infoHash) + { + var historyItem = _downloadHistoryService.GetLatestDownloadHistoryItem(infoHash); + + if (historyItem == null) + { + _logger.Debug("No download history item for infohash {0}, unable to provide seed configuration", infoHash); + return null; + } + + var parsedEpisodeInfo = Parser.Parser.ParseTitle(historyItem.Release.Title); + + if (parsedEpisodeInfo == null) + { + _logger.Debug("No parsed title in download history item item for infohash {0}, unable to provide seed configuration", infoHash); + return null; + } + + return new CachedSeedConfiguration + { + IndexerId = historyItem.IndexerId, + FullSeason = parsedEpisodeInfo.FullSeason + }; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/SeedConfigProvider.cs b/src/NzbDrone.Core/Indexers/SeedConfigProvider.cs index ff07ec2b3..675bbf1f5 100644 --- a/src/NzbDrone.Core/Indexers/SeedConfigProvider.cs +++ b/src/NzbDrone.Core/Indexers/SeedConfigProvider.cs @@ -1,9 +1,8 @@ using System; -using System.Linq; -using NzbDrone.Common.Extensions; +using NzbDrone.Common.Cache; using NzbDrone.Core.Datastore; using NzbDrone.Core.Download.Clients; -using NzbDrone.Core.Indexers; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers @@ -11,15 +10,20 @@ namespace NzbDrone.Core.Indexers public interface ISeedConfigProvider { TorrentSeedConfiguration GetSeedConfiguration(RemoteEpisode release); + TorrentSeedConfiguration GetSeedConfiguration(int indexerId, bool fullSeason); } - public class SeedConfigProvider : ISeedConfigProvider + public class SeedConfigProvider : ISeedConfigProvider, IHandle { private readonly IIndexerFactory _indexerFactory; - public SeedConfigProvider(IIndexerFactory indexerFactory) + private readonly ICached _cache; + + public SeedConfigProvider(IIndexerFactory indexerFactory, ICacheManager cacheManager) { _indexerFactory = indexerFactory; + + _cache = cacheManager.GetRollingCache(GetType(), "criteriaByIndexer", TimeSpan.FromHours(1)); } public TorrentSeedConfiguration GetSeedConfiguration(RemoteEpisode remoteEpisode) @@ -27,33 +31,49 @@ namespace NzbDrone.Core.Indexers if (remoteEpisode.Release.DownloadProtocol != DownloadProtocol.Torrent) return null; if (remoteEpisode.Release.IndexerId == 0) return null; + return GetSeedConfiguration(remoteEpisode.Release.IndexerId, remoteEpisode.ParsedEpisodeInfo.FullSeason); + } + + public TorrentSeedConfiguration GetSeedConfiguration(int indexerId, bool fullSeason) + { + if (indexerId == 0) return null; + + var seedCriteria = _cache.Get(indexerId.ToString(), () => FetchSeedCriteria(indexerId)); + + if (seedCriteria == null) return null; + + var seedConfig = new TorrentSeedConfiguration + { + Ratio = seedCriteria.SeedRatio + }; + + var seedTime = fullSeason ? seedCriteria.SeasonPackSeedTime : seedCriteria.SeedTime; + if (seedTime.HasValue) + { + seedConfig.SeedTime = TimeSpan.FromMinutes(seedTime.Value); + } + + return seedConfig; + } + + private SeedCriteriaSettings FetchSeedCriteria(int indexerId) + { try { - var indexer = _indexerFactory.Get(remoteEpisode.Release.IndexerId); + var indexer = _indexerFactory.Get(indexerId); var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings; - if (torrentIndexerSettings != null && torrentIndexerSettings.SeedCriteria != null) - { - var seedConfig = new TorrentSeedConfiguration - { - Ratio = torrentIndexerSettings.SeedCriteria.SeedRatio - }; - - var seedTime = remoteEpisode.ParsedEpisodeInfo.FullSeason ? torrentIndexerSettings.SeedCriteria.SeasonPackSeedTime : torrentIndexerSettings.SeedCriteria.SeedTime; - if (seedTime.HasValue) - { - seedConfig.SeedTime = TimeSpan.FromMinutes(seedTime.Value); - } - - return seedConfig; - } + return torrentIndexerSettings?.SeedCriteria; } catch (ModelNotFoundException) { return null; } + } - return null; + public void Handle(IndexerSettingUpdatedEvent message) + { + _cache.Clear(); } } }