diff --git a/src/NzbDrone.Core/Indexers/Definitions/SuperBits.cs b/src/NzbDrone.Core/Indexers/Definitions/SuperBits.cs new file mode 100644 index 000000000..75551b555 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/SuperBits.cs @@ -0,0 +1,350 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Linq; +using FluentValidation; +using Newtonsoft.Json; +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.Indexers.Definitions +{ + public class SuperBits : HttpIndexerBase + { + public override string Name => "SuperBits"; + + public override string BaseUrl => "https://superbits.org/"; + public override DownloadProtocol Protocol => DownloadProtocol.Torrent; + public override IndexerPrivacy Privacy => IndexerPrivacy.Private; + public override IndexerCapabilities Capabilities => SetCapabilities(); + + public SuperBits(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + : base(httpClient, indexerStatusService, configService, logger) + { + } + + public override IIndexerRequestGenerator GetRequestGenerator() + { + return new SuperBitsRequestGenerator() { Settings = Settings, Capabilities = Capabilities, BaseUrl = BaseUrl }; + } + + public override IParseIndexerResponse GetParser() + { + return new SuperBitsParser(Settings, Capabilities.Categories, BaseUrl); + } + + private IndexerCapabilities SetCapabilities() + { + var caps = new IndexerCapabilities + { + TvSearchParams = new List + { + TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep + }, + MovieSearchParams = new List + { + MovieSearchParam.Q, MovieSearchParam.ImdbId + }, + MusicSearchParams = new List + { + MusicSearchParam.Q + }, + BookSearchParams = new List + { + BookSearchParam.Q + } + }; + + caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.MoviesDVD, "DVD-R Swesub"); + caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TV, "DVD-R TV"); + caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.BooksEBook, "eBok"); + caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.MoviesHD, "Film 1080"); + caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Movies3D, "Film 3D"); + caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.MoviesHD, "Film 720"); + caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.MoviesBluRay, "Film Bluray"); + caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.TV, "Svensk TV"); + caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.AudioAudiobook, "Ljudböcker"); + caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.AudioVideo, "Musikvideos"); + caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.BooksMags, "E-tidningar"); + caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.Audio, "Musik"); + caps.Categories.AddCategoryMapping(13, NewznabStandardCategory.Other, "Omslag"); + caps.Categories.AddCategoryMapping(14, NewznabStandardCategory.Other, "Övrigt"); + caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.PCGames, "PC-Spel"); + caps.Categories.AddCategoryMapping(16, NewznabStandardCategory.PC0day, "Program"); + caps.Categories.AddCategoryMapping(17, NewznabStandardCategory.ConsolePS3, "PS3"); + caps.Categories.AddCategoryMapping(18, NewznabStandardCategory.TV, "TV"); + caps.Categories.AddCategoryMapping(19, NewznabStandardCategory.ConsoleWii, "Wii"); + caps.Categories.AddCategoryMapping(20, NewznabStandardCategory.ConsoleXBox, "Xbox"); + caps.Categories.AddCategoryMapping(21, NewznabStandardCategory.MoviesOther, "Xvid"); + caps.Categories.AddCategoryMapping(22, NewznabStandardCategory.XXX, "XXX"); + caps.Categories.AddCategoryMapping(24, NewznabStandardCategory.MoviesUHD, "Film 4K"); + caps.Categories.AddCategoryMapping(26, NewznabStandardCategory.TV, "TV DK"); + caps.Categories.AddCategoryMapping(27, NewznabStandardCategory.TV, "TV NO"); + caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.TV, "TV FI"); + return caps; + } + } + + public class SuperBitsRequestGenerator : IIndexerRequestGenerator + { + public SuperBitsSettings Settings { get; set; } + public IndexerCapabilities Capabilities { get; set; } + public string BaseUrl { get; set; } + + public SuperBitsRequestGenerator() + { + } + + private IEnumerable GetPagedRequests(string term, int[] categories, string imdbId = null) + { + var searchUrl = BaseUrl + "api/v1/torrents"; + + // And this was option one from + // https://github.com/Jackett/Jackett/pull/7166#discussion_r376817517 + var queryCollection = new NameValueCollection(); + var searchString = term; + + queryCollection.Add("extendedSearch", "false"); + queryCollection.Add("freeleech", "false"); + queryCollection.Add("index", "0"); + queryCollection.Add("limit", "100"); + queryCollection.Add("order", "desc"); + queryCollection.Add("page", "search"); + + if (imdbId.IsNotNullOrWhiteSpace()) + { + queryCollection.Add("searchText", imdbId); + } + else + { + queryCollection.Add("searchText", searchString); + } + + queryCollection.Add("sort", "d"); + queryCollection.Add("section", "all"); + queryCollection.Add("stereoscopic", "false"); + queryCollection.Add("sweaudio", "false"); + queryCollection.Add("swesub", "false"); + queryCollection.Add("watchview", "false"); + + searchUrl += "?" + queryCollection.GetQueryString(); + + foreach (var cat in Capabilities.Categories.MapTorznabCapsToTrackers(categories)) + { + searchUrl += "&categories[]=" + cat; + } + + var request = new IndexerRequest(searchUrl, HttpAccept.Json); + + request.HttpRequest.Headers.Add("Referer", BaseUrl); + + foreach (var cookie in CookieUtil.CookieHeaderToDictionary(Settings.Cookie)) + { + request.HttpRequest.Cookies.Add(cookie.Key, cookie.Value); + } + + yield return request; + } + + public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.ImdbId)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.ImdbId)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories)); + + return pageableRequests; + } + + public Func> GetCookies { get; set; } + public Action, DateTime?> CookiesUpdater { get; set; } + } + + public class SuperBitsParser : IParseIndexerResponse + { + private readonly SuperBitsSettings _settings; + private readonly IndexerCapabilitiesCategories _categories; + private readonly string _baseUrl; + + public SuperBitsParser(SuperBitsSettings settings, IndexerCapabilitiesCategories categories, string baseUrl) + { + _settings = settings; + _categories = categories; + _baseUrl = baseUrl; + } + + public IList ParseResponse(IndexerResponse indexerResponse) + { + var torrentInfos = new List(); + + var json = JsonConvert.DeserializeObject(indexerResponse.Content); + foreach (var row in json ?? Enumerable.Empty()) + { + var release = new TorrentInfo(); + var descriptions = new List(); + var tags = new List(); + + release.MinimumRatio = 1.1; + release.MinimumSeedTime = 172800; // 48 hours + release.Title = row.name; + release.Category = _categories.MapTrackerCatToNewznab(row.category.ToString()); + release.Size = row.size; + release.Seeders = row.seeders; + release.Peers = row.leechers + release.Seeders; + release.PublishDate = DateTime.ParseExact(row.added.ToString() + " +01:00", "yyyy-MM-dd HH:mm:ss zzz", CultureInfo.InvariantCulture); + release.Files = row.numfiles; + release.Grabs = row.times_completed; + + release.InfoUrl = _baseUrl + "torrent/" + row.id.ToString() + "/"; + release.Guid = release.InfoUrl; + release.DownloadUrl = _baseUrl + "api/v1/torrents/download/" + row.id.ToString(); + + if (row.frileech == 1) + { + release.DownloadVolumeFactor = 0; + } + else + { + release.DownloadVolumeFactor = 1; + } + + release.UploadVolumeFactor = 1; + + //if (!string.IsNullOrWhiteSpace(row.customcover.ToString())) + //{ + // release.Poster = new Uri(SiteLink + row.customcover); + //} + if (row.imdbid2 != null && row.imdbid2.ToString().StartsWith("tt")) + { + release.ImdbId = ParseUtil.CoerceInt(row.imdbid2.ToString().Substring(2)); + descriptions.Add("Title: " + row.title); + descriptions.Add("Year: " + row.year); + descriptions.Add("Genres: " + row.genres); + descriptions.Add("Tagline: " + row.tagline); + descriptions.Add("Cast: " + row.cast); + descriptions.Add("Rating: " + row.rating); + descriptions.Add("Plot: " + row.plot); + + //release.Poster = new Uri(SiteLink + "img/imdb/" + row.imdbid2 + ".jpg"); + } + + if ((int)row.p2p == 1) + { + tags.Add("P2P"); + } + + if ((int)row.pack == 1) + { + tags.Add("Pack"); + } + + if ((int)row.reqid != 0) + { + tags.Add("Request"); + } + + if ((int)row.sweaudio != 0) + { + tags.Add("Swedish audio"); + } + + if ((int)row.swesub != 0) + { + tags.Add("Swedish subtitles"); + } + + if (tags.Count > 0) + { + descriptions.Add("Tags: " + string.Join(", ", tags)); + } + + var preDate = row.preDate.ToString(); + if (!string.IsNullOrWhiteSpace(preDate) && preDate != "1970-01-01 01:00:00") + { + descriptions.Add("PRE: " + preDate); + } + + descriptions.Add("Section: " + row.section); + + //release.Description = string.Join("
\n", descriptions); + torrentInfos.Add(release); + } + + return torrentInfos.ToArray(); + } + + public Action, DateTime?> CookiesUpdater { get; set; } + } + + public class SuperBitsSettingsValidator : AbstractValidator + { + public SuperBitsSettingsValidator() + { + RuleFor(c => c.Cookie).NotEmpty(); + } + } + + public class SuperBitsSettings : IProviderConfig + { + private static readonly SuperBitsSettingsValidator Validator = new SuperBitsSettingsValidator(); + + public SuperBitsSettings() + { + Cookie = ""; + } + + public string BaseUrl { get; set; } + + [FieldDefinition(1, Label = "Cookie", HelpText = "Site Cookie")] + public string Cookie { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +}