diff --git a/README.md b/README.md index b82168956..ec0adcc26 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ We were previously focused on TV but are working on extending searches to allow * [MoreThan.tv](https://morethan.tv/) * [pretome](https://pretome.info) * [PrivateHD](https://privatehd.to/) + * [RARGB](https://rarbg.to/) * [RuTor](http://rutor.org/) * [SceneAccess](https://sceneaccess.eu/login) * [SceneTime](https://www.scenetime.com/) diff --git a/src/Jackett/Content/logos/rarbg.png b/src/Jackett/Content/logos/rarbg.png new file mode 100644 index 000000000..da8bebbbc Binary files /dev/null and b/src/Jackett/Content/logos/rarbg.png differ diff --git a/src/Jackett/Indexers/BaseIndexer.cs b/src/Jackett/Indexers/BaseIndexer.cs index dd528f4b6..f5cd64976 100644 --- a/src/Jackett/Indexers/BaseIndexer.cs +++ b/src/Jackett/Indexers/BaseIndexer.cs @@ -38,7 +38,7 @@ namespace Jackett.Indexers set { configData.CookieHeader.Value = value; } } - + protected ConfigurationData configData; @@ -70,7 +70,7 @@ namespace Jackett.Indexers { if (string.IsNullOrEmpty(downloadUrlBase)) return releases; - foreach(var release in releases) + foreach (var release in releases) { if (release.Link.ToString().StartsWith(downloadUrlBase)) { @@ -403,12 +403,34 @@ namespace Jackett.Indexers categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory)); } + protected void AddMultiCategoryMapping(TorznabCategory newznabCategory, params int[] trackerCategories) + { + foreach (var trackerCat in trackerCategories) + { + categoryMapping.Add(new CategoryMapping(trackerCat.ToString(), newznabCategory.ID)); + } + } + + protected void AddMultiCategoryMapping(int trackerCategory, params TorznabCategory[] newznabCategories) + { + foreach (var newznabCat in newznabCategories) + { + categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCat.ID)); + } + } + protected List MapTorznabCapsToTrackers(TorznabQuery query) { var result = new List(); foreach (var cat in query.Categories) { - foreach (var mapping in categoryMapping.Where(c => c.NewzNabCategory == cat)) + var queryCats = new List { cat }; + var newznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.ID == cat); + if (newznabCat != null) + { + queryCats.AddRange(newznabCat.SubCategories.Select(c => c.ID)); + } + foreach (var mapping in categoryMapping.Where(c => queryCats.Contains(c.NewzNabCategory))) { result.Add(mapping.TrackerCategory); } diff --git a/src/Jackett/Indexers/Rarbg.cs b/src/Jackett/Indexers/Rarbg.cs new file mode 100644 index 000000000..7b0334544 --- /dev/null +++ b/src/Jackett/Indexers/Rarbg.cs @@ -0,0 +1,162 @@ +using Jackett.Models; +using Jackett.Models.IndexerConfig; +using Jackett.Services; +using Jackett.Utils.Clients; +using Newtonsoft.Json.Linq; +using NLog; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using System.Web; + +namespace Jackett.Indexers +{ + public class Rarbg : BaseIndexer, IIndexer + { + readonly static string defaultSiteLink = "https://torrentapi.org/"; + + private Uri BaseUri + { + get { return new Uri(configData.Url.Value); } + set { configData.Url.Value = value.ToString(); } + } + + private string ApiEndpoint { get { return BaseUri + "pubapi_v2.php"; } } + private string TokenUrl { get { return ApiEndpoint + "?get_token=get_token"; } } + private string SearchUrl { get { return ApiEndpoint + "?app_id=jackett_v{0}&mode={1}&format=json_extended&search_string={2}&token={3}"; } } + + + new ConfigurationDataUrl configData + { + get { return (ConfigurationDataUrl)base.configData; } + set { base.configData = value; } + } + + private DateTime lastTokenFetch; + private string token; + + readonly TimeSpan TOKEN_DURATION = TimeSpan.FromMinutes(10); + + private bool HasValidToken { get { return !string.IsNullOrEmpty(token) && lastTokenFetch > DateTime.Now - TOKEN_DURATION; } } + + Dictionary categoryLabels; + + public Rarbg(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps) + : base(name: "RARBG", + description: "RARBG", + link: defaultSiteLink, + caps: new TorznabCapabilities(), + manager: i, + client: wc, + logger: l, + p: ps, + configData: new ConfigurationDataUrl(defaultSiteLink)) + { + categoryLabels = new Dictionary(); + + AddCat(4, TorznabCatType.XXX, "XXX (18+)"); + AddCat(14, TorznabCatType.MoviesSD, "Movies/XVID"); + AddCat(48, TorznabCatType.MoviesHD, "Movies/XVID/720"); + AddCat(17, TorznabCatType.MoviesSD, "Movies/x264"); + AddCat(44, TorznabCatType.MoviesHD, "Movies/x264/1080"); + AddCat(45, TorznabCatType.MoviesHD, "Movies/x264/720"); + AddCat(47, TorznabCatType.Movies3D, "Movies/x264/3D"); + AddCat(42, TorznabCatType.MoviesBluRay, "Movies/Full BD"); + AddCat(46, TorznabCatType.MoviesBluRay, "Movies/BD Remux"); + AddCat(18, TorznabCatType.TVSD, "TV Episodes"); + AddCat(41, TorznabCatType.TVHD, "TV HD Episodes"); + AddCat(23, TorznabCatType.AudioMP3, "Music/MP3"); + AddCat(25, TorznabCatType.AudioLossless, "Music/FLAC"); + AddCat(27, TorznabCatType.PCGames, "Games/PC ISO"); + AddCat(28, TorznabCatType.PCGames, "Games/PC RIP"); + AddCat(40, TorznabCatType.ConsolePS3, "Games/PS3"); + AddCat(32, TorznabCatType.ConsoleXbox360, "Games/XBOX-360"); + AddCat(33, TorznabCatType.PCISO, "Software/PC ISO"); + AddCat(35, TorznabCatType.BooksEbook, "e-Books"); + } + + void AddCat(int cat, TorznabCategory catType, string label) + { + AddCategoryMapping(cat, catType); + categoryLabels.Add(label, cat); + } + + async Task CheckToken() + { + if (!HasValidToken) + { + var result = await RequestStringWithCookiesAndRetry(TokenUrl); + var json = JObject.Parse(result.Content); + token = json.Value("token"); + lastTokenFetch = DateTime.Now; + } + } + + public async Task ApplyConfiguration(JToken configJson) + { + configData.LoadValuesFromJson(configJson); + var releases = await PerformQuery(new TorznabQuery()); + + await ConfigureIfOK(string.Empty, releases.Count() > 0, () => + { + throw new Exception("Could not find releases from this URL"); + }); + } + + public async Task> PerformQuery(TorznabQuery query) + { + await CheckToken(); + var releases = new List(); + var queryStr = HttpUtility.UrlEncode(query.GetQueryString()); + + var mode = string.IsNullOrEmpty(queryStr) ? "list" : "search"; + var episodeSearchUrl = string.Format(SearchUrl, Engine.ConfigService.GetVersion(), mode, queryStr, token); + var cats = string.Join(";", MapTorznabCapsToTrackers(query)); + if (!string.IsNullOrEmpty(cats)) + { + episodeSearchUrl += "&category=" + cats; + } + var response = await RequestStringWithCookiesAndRetry(episodeSearchUrl, string.Empty); + + try + { + var jsonContent = JObject.Parse(response.Content); + + if (jsonContent.Value("error_code") == 20) // no results found + { + return releases.ToArray(); + } + + foreach (var item in jsonContent.Value("torrent_results")) + { + var release = new ReleaseInfo(); + release.Title = item.Value("title"); + release.Description = release.Title; + release.Category = MapTrackerCatToNewznab(categoryLabels[item.Value("category")].ToString()); + release.Link = new Uri(item.Value("download")); + release.Comments = new Uri(item.Value("info_page")); + release.Guid = release.Comments; + + // ex: 2015-08-16 21:25:08 +0000 + var dateStr = item.Value("pubdate").Replace(" +0000", ""); + var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); + release.PublishDate = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc).ToLocalTime(); + + release.Seeders = item.Value("seeders"); + release.Peers = item.Value("leechers") + release.Seeders; + release.Size = item.Value("size"); + releases.Add(release); + } + } + catch (Exception ex) + { + OnParseError(response.Content, ex); + } + + return releases.ToArray(); + } + + } +} diff --git a/src/Jackett/Jackett.csproj b/src/Jackett/Jackett.csproj index 3bb428742..4ea72ab0a 100644 --- a/src/Jackett/Jackett.csproj +++ b/src/Jackett/Jackett.csproj @@ -190,6 +190,7 @@ + @@ -438,6 +439,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest