You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Prowlarr/src/NzbDrone.Core/Indexers/Definitions/Milkie.cs

269 lines
9.6 KiB

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using FluentValidation;
using Newtonsoft.Json;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class Milkie : TorrentIndexerBase<MilkieSettings>
{
public override string Name => "Milkie";
public override string BaseUrl => "https://milkie.cc/";
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override IndexerCapabilities Capabilities => SetCapabilities();
public Milkie(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new MilkieRequestGenerator() { Settings = Settings, Capabilities = Capabilities, BaseUrl = BaseUrl };
}
public override IParseIndexerResponse GetParser()
{
return new MilkieParser(Settings, Capabilities.Categories, BaseUrl);
}
private IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q
},
BookSearchParams = new List<BookSearchParam>
{
BookSearchParam.Q
}
};
caps.Categories.AddCategoryMapping("1", NewznabStandardCategory.Movies, "Movies");
caps.Categories.AddCategoryMapping("2", NewznabStandardCategory.TV, "TV");
caps.Categories.AddCategoryMapping("3", NewznabStandardCategory.Audio, "Music");
caps.Categories.AddCategoryMapping("4", NewznabStandardCategory.PCGames, "Games");
caps.Categories.AddCategoryMapping("5", NewznabStandardCategory.Books, "Ebook");
caps.Categories.AddCategoryMapping("6", NewznabStandardCategory.PC, "Apps");
caps.Categories.AddCategoryMapping("7", NewznabStandardCategory.XXX, "Adult");
return caps;
}
}
public class MilkieRequestGenerator : IIndexerRequestGenerator
{
public MilkieSettings Settings { get; set; }
public IndexerCapabilities Capabilities { get; set; }
public string BaseUrl { get; set; }
public MilkieRequestGenerator()
{
}
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
{
var searchUrl = BaseUrl + "api/v1/torrents";
var qc = new NameValueCollection
{
{ "ps", "100" }
};
if (!string.IsNullOrWhiteSpace(term))
{
qc.Add("query", term);
}
if (categories != null && categories.Length > 0)
{
qc.Add("categories", string.Join(",", Capabilities.Categories.MapTorznabCapsToTrackers(categories)));
}
searchUrl = searchUrl + "?" + qc.GetQueryString();
var request = new IndexerRequest(searchUrl, HttpAccept.Json);
request.HttpRequest.Headers.Add("x-milkie-auth", Settings.ApiKey);
yield return request;
}
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories));
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories));
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories));
return pageableRequests;
}
public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}
public class MilkieParser : IParseIndexerResponse
{
private readonly MilkieSettings _settings;
private readonly IndexerCapabilitiesCategories _categories;
private readonly string _baseUrl;
public MilkieParser(MilkieSettings settings, IndexerCapabilitiesCategories categories, string baseUrl)
{
_settings = settings;
_categories = categories;
_baseUrl = baseUrl;
}
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<TorrentInfo>();
var response = JsonConvert.DeserializeObject<MilkieResponse>(indexerResponse.Content);
var dlQueryParams = new NameValueCollection
{
{ "key", _settings.ApiKey }
};
foreach (var torrent in response.Torrents)
{
var torrentUrl = _baseUrl + "api/v1/torrents";
var link = $"{torrentUrl}/{torrent.Id}/torrent?{dlQueryParams.GetQueryString()}";
var details = $"{_baseUrl}browse/{torrent.Id}";
var publishDate = DateTimeUtil.FromUnknown(torrent.CreatedAt);
var release = new TorrentInfo
{
Title = torrent.ReleaseName,
DownloadUrl = link,
InfoUrl = details,
Guid = details,
PublishDate = publishDate,
Category = _categories.MapTrackerCatToNewznab(torrent.Category.ToString()),
Size = torrent.Size,
Seeders = torrent.Seeders,
Peers = torrent.Seeders + torrent.PartialSeeders + torrent.Leechers,
Grabs = torrent.Downloaded,
UploadVolumeFactor = 1,
DownloadVolumeFactor = 0,
MinimumRatio = 1,
MinimumSeedTime = 172800 // 48 hours
};
torrentInfos.Add(release);
}
return torrentInfos.ToArray();
}
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}
public class MilkieSettingsValidator : AbstractValidator<MilkieSettings>
{
public MilkieSettingsValidator()
{
RuleFor(c => c.ApiKey).NotEmpty();
}
}
public class MilkieSettings : IProviderConfig
{
private static readonly MilkieSettingsValidator Validator = new MilkieSettingsValidator();
public MilkieSettings()
{
ApiKey = "";
}
public string BaseUrl { get; set; }
[FieldDefinition(1, Label = "Apikey", HelpText = "Site ApiKey", Privacy = PrivacyLevel.ApiKey)]
public string ApiKey { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
public class MilkieResponse
{
public int Hits { get; set; }
public int Took { get; set; }
public MilkieTorrent[] Torrents { get; set; }
}
public class MilkieTorrent
{
public string Id { get; set; }
public string ReleaseName { get; set; }
public int Category { get; set; }
public int Downloaded { get; set; }
public int Seeders { get; set; }
public int PartialSeeders { get; set; }
public int Leechers { get; set; }
public long Size { get; set; }
public string CreatedAt { get; set; }
}
}