diff --git a/src/Jackett/Content/logos/ilovetorrents.png b/src/Jackett/Content/logos/ilovetorrents.png new file mode 100644 index 000000000..b85a68bac Binary files /dev/null and b/src/Jackett/Content/logos/ilovetorrents.png differ diff --git a/src/Jackett/Indexers/ILoveTorrents.cs b/src/Jackett/Indexers/ILoveTorrents.cs new file mode 100644 index 000000000..31dd2c4ac --- /dev/null +++ b/src/Jackett/Indexers/ILoveTorrents.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CsQuery; +using CsQuery.ExtensionMethods.Internal; +using Jackett.Models; +using Jackett.Models.IndexerConfig; +using Jackett.Services; +using Jackett.Utils; +using Jackett.Utils.Clients; +using Newtonsoft.Json.Linq; +using NLog; + +namespace Jackett.Indexers +{ + // ReSharper disable once InconsistentNaming + public class ILoveTorrents : BaseIndexer, IIndexer + { + private string BrowseUrl => SiteLink + "browse.php"; + private string LoginUrl => SiteLink + "takelogin.php"; + + new ConfigurationDataBasicLogin configData + { + get { return (ConfigurationDataBasicLogin)base.configData; } + set { base.configData = value; } + } + + public ILoveTorrents(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps) + : base(name: "ILoveTorrents", + description: "ILT", + link: "https://www.ilovetorrents.me/", + caps: TorznabUtil.CreateDefaultTorznabTVCaps(), + manager: i, + client: wc, + logger: l, + p: ps, + configData: new ConfigurationDataBasicLogin()) + { + AddCategoryMapping(7, TorznabCatType.TV); + AddCategoryMapping(8, TorznabCatType.TVHD); + + AddCategoryMapping(80, TorznabCatType.MoviesBluRay); + AddCategoryMapping(20, TorznabCatType.MoviesDVD); + AddCategoryMapping(41, TorznabCatType.MoviesHD); + AddCategoryMapping(19, TorznabCatType.Movies); + } + + public async Task ApplyConfiguration(JToken configJson) + { + configData.LoadValuesFromJson(configJson); + var pairs = new Dictionary { + { "username", configData.Username.Value }, + { "password", configData.Password.Value }, + { "returnto", "/" }, + { "login", "Log in!" } + }; + + var loginPage = await RequestStringWithCookies(SiteLink, string.Empty); + + var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, SiteLink, SiteLink); + await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () => + { + CQ dom = result.Content; + var messageEl = dom["body > div"].First(); + var errorMessage = messageEl.Text().Trim(); + throw new ExceptionWithConfigData(errorMessage, configData); + }); + return IndexerConfigurationStatus.RequiresTesting; + } + + public async Task> PerformQuery(TorznabQuery query) + { + var releases = new List(); + var searchString = query.GetQueryString(); + var searchUrl = BrowseUrl; + var trackerCats = MapTorznabCapsToTrackers(query); + var queryCollection = new NameValueCollection(); + + // Tracker can only search OR return things in categories + if (!string.IsNullOrWhiteSpace(searchString)) + { + queryCollection.Add("search", searchString); + queryCollection.Add("cat", "0"); + } + else + { + foreach (var cat in MapTorznabCapsToTrackers(query)) + { + queryCollection.Add("c" + cat, "1"); + } + + queryCollection.Add("incldead", "0"); + } + + searchUrl += "?" + queryCollection.GetQueryString(); + + await ProcessPage(releases, searchUrl); + return releases; + } + + private async Task ProcessPage(List releases, string searchUrl) + { + var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl); + var results = response.Content; + try + { + CQ dom = results; + + var rows = dom[".koptekst tr"]; + foreach (var row in rows.Skip(1)) + { + var release = new ReleaseInfo(); + + var link = row.Cq().Find("td:eq(2) a:eq(0)").First(); + var text = row.Cq().Find("td:eq(1) a:eq(0)").First(); + release.Guid = new Uri(SiteLink + link.Attr("href")); + release.Comments = release.Guid; + release.Title = text.Text().Trim(); //link.Text().Trim(); + release.Description = release.Title; + + // If we search an get no results, we still get a table just with no info. + if (string.IsNullOrWhiteSpace(release.Title)) + { + break; + } + + // Check if the release has been assigned a category + if (row.Cq().Find("td:eq(0) a").Length > 0) + { + var cat = row.Cq().Find("td:eq(0) a").First().Attr("href").Substring(15); + release.Category = MapTrackerCatToNewznab(cat); + } + + var qLink = row.Cq().Find("td:eq(1) a").First(); + release.Link = new Uri(SiteLink + qLink.Attr("href")); + + var added = row.Cq().Find("td:eq(7)").First().Text().Trim(); + var date = added.Substring(0, 10); + var time = added.Substring(12, 8); + var dateTime = date + time; + release.PublishDate = DateTime.ParseExact(dateTime, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime(); + + var sizeStr = row.Cq().Find("td:eq(8)").First().Text().Trim(); + release.Size = ReleaseInfo.GetBytes(sizeStr); + + release.Seeders = ParseUtil.CoerceInt(row.Cq().Find("td:eq(10)").First().Text().Trim()); + release.Peers = ParseUtil.CoerceInt(row.Cq().Find("td:eq(11)").First().Text().Trim()) + release.Seeders; + + releases.Add(release); + } + } + catch (Exception ex) + { + OnParseError(results, ex); + } + } + } +} diff --git a/src/Jackett/Jackett.csproj b/src/Jackett/Jackett.csproj index 3ce9f8f8f..78732f22e 100644 --- a/src/Jackett/Jackett.csproj +++ b/src/Jackett/Jackett.csproj @@ -201,6 +201,7 @@ + @@ -478,6 +479,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest