Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added src/Jackett/Content/logos/gftracker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
202 changes: 202 additions & 0 deletions src/Jackett/Indexers/GFTracker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
using CsQuery;
using Jackett.Models;
using Jackett.Services;
using Jackett.Utils;
using Jackett.Utils.Clients;
using Newtonsoft.Json.Linq;
using NLog;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Jackett.Models.IndexerConfig;
using System.Collections.Specialized;

namespace Jackett.Indexers
{
//
// Quick and dirty indexer for GFTracker.
//
public class GFTracker : BaseIndexer, IIndexer
{
private string StartPageUrl { get { return SiteLink + "login.php?returnto=%2F"; } }
private string LoginUrl { get { return SiteLink + "loginsite.php"; } }
private string SearchUrl { get { return SiteLink + "browse.php"; } }

new ConfigurationDataRecaptchaLogin configData
{
get { return (ConfigurationDataRecaptchaLogin)base.configData; }
set { base.configData = value; }
}

public GFTracker(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
: base(name: "GFTracker",
description: "Home of user happiness",
link: "https://www.thegft.org/",
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
manager: i,
client: w,
logger: l,
p: ps,
configData: new ConfigurationDataRecaptchaLogin())
{
AddCategoryMapping(4, TorznabCatType.TV); // TV/XVID
AddCategoryMapping(17, TorznabCatType.TVHD); // TV/X264
AddCategoryMapping(19, TorznabCatType.TV); // TV/DVDRIP
AddCategoryMapping(26, TorznabCatType.TVHD); // TV/BLURAY
AddCategoryMapping(37, TorznabCatType.TV); // TV/DVDR
AddCategoryMapping(47, TorznabCatType.TV); // TV/SD

AddCategoryMapping(7, TorznabCatType.Movies); // Movies/XVID
AddCategoryMapping(8, TorznabCatType.MoviesDVD); // Movies/DVDR
AddCategoryMapping(12, TorznabCatType.MoviesBluRay); // Movies/BLURAY
AddCategoryMapping(18, TorznabCatType.MoviesHD); // Movies/X264-HD
AddCategoryMapping(49, TorznabCatType.MoviesSD); // Movies/X264-SD
}

public override async Task<ConfigurationData> GetConfigurationForSetup()
{
var loginPage = await RequestStringWithCookies(StartPageUrl, string.Empty);
CQ cq = loginPage.Content;
var result = new ConfigurationDataRecaptchaLogin();
CQ recaptcha = cq.Find(".g-recaptcha").Attr("data-sitekey");
if(recaptcha.Length != 0) // recaptcha not always present in login form, perhaps based on cloudflare uid or just phase of the moon
{
result.CookieHeader.Value = loginPage.Cookies;
result.Captcha.SiteKey = cq.Find(".g-recaptcha").Attr("data-sitekey");
return result;
} else
{
var stdResult = new ConfigurationDataBasicLogin();
stdResult.CookieHeader.Value = loginPage.Cookies;
return stdResult;
}
}

public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
{
configData.LoadValuesFromJson(configJson);
var pairs = new Dictionary<string, string> {
{ "username", configData.Username.Value },
{ "password", configData.Password.Value },
{ "g-recaptcha-response", configData.Captcha.Value }
};

if (!string.IsNullOrWhiteSpace(configData.Captcha.Cookie))
{
CookieHeader = configData.Captcha.Cookie;
try
{
var results = await PerformQuery(new TorznabQuery());
if (results.Count() == 0)
{
throw new Exception("Your cookie did not work");
}

SaveConfig();
IsConfigured = true;
return IndexerConfigurationStatus.Completed;
}
catch (Exception e)
{
IsConfigured = false;
throw new Exception("Your cookie did not work: " + e.Message);
}
}

var cookieJar = configData.CookieHeader.Value;
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, configData.CookieHeader.Value, true, null, StartPageUrl);
cookieJar += response.Cookies.ToString();
response = await RequestStringWithCookiesAndRetry(SearchUrl, cookieJar);

await ConfigureIfOK(cookieJar, response.Content != null && response.Content.Contains("logout.php"), () =>
{
CQ dom = response.Content;
var messageEl = dom["h2"].Last();
messageEl.Children("a").Remove();
messageEl.Children("style").Remove();
var errorMessage = messageEl.Text().Trim();
throw new ExceptionWithConfigData(errorMessage, configData);
});
return IndexerConfigurationStatus.RequiresTesting;
}

public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
{
var releases = new List<ReleaseInfo>();
var searchString = query.GetQueryString();
var queryCollection = new NameValueCollection();

queryCollection.Add("view", "0");
if (!string.IsNullOrWhiteSpace(searchString))
{
queryCollection.Add("search", searchString);
}

foreach (var cat in MapTorznabCapsToTrackers(query))
{
queryCollection.Add(string.Format("c{0}", cat), "1");
}

var searchUrl = SearchUrl + "?" + queryCollection.GetQueryString();

var results = await RequestStringWithCookiesAndRetry(searchUrl, CookieHeader);
try
{
CQ dom = results.Content;
var rows = dom["#torrentBrowse > table > tbody > tr"];
foreach (var row in rows.Skip(1))
{
var release = new ReleaseInfo();
CQ qRow = row.Cq();

//CQ qLink = qRow.Children().ElementAt(1).Cq().Children("a").ElementAt(1).Cq();
CQ qLink;
CQ qTmp = qRow.Children().ElementAt(1).Cq().Find("a");
if (qTmp.Length < 2) {
qLink = qRow.Children().ElementAt(1).Cq().Find("a").ElementAt(0).Cq();
} else {
qLink = qRow.Children().ElementAt(1).Cq().Find("a").ElementAt(1).Cq();
}

release.MinimumRatio = 1;
release.MinimumSeedTime = 172800;
release.Title = qLink.Attr("title");
release.Description = release.Title;
release.Guid = new Uri(SiteLink + qLink.Attr("href").TrimStart('/'));
release.Comments = release.Guid;

qLink = qRow.Children().ElementAt(3).Cq().Children("a").First();
release.Link = new Uri(string.Format("{0}{1}", SiteLink, qLink.Attr("href")));

var catUrl = qRow.Children().ElementAt(0).FirstElementChild.Cq().Attr("href");
var catNum = catUrl.Split(new char[] { '=', '&' })[2].Replace("c", "");
release.Category = MapTrackerCatToNewznab(catNum);

var dateString = qRow.Children().ElementAt(6).Cq().Text().Trim();
//var pubDate = DateTime.ParseExact(dateString, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
release.PublishDate = Jackett.Utils.DateTimeUtil.FromTimeAgo(dateString);

var sizeStr = qRow.Children().ElementAt(7).Cq().Text().Split(new char[] { '/' })[0];
release.Size = ReleaseInfo.GetBytes(sizeStr);

release.Seeders = ParseUtil.CoerceInt(qRow.Children().ElementAt(8).Cq().Text().Split(new char[] { '/' })[0].Trim());
release.Peers = ParseUtil.CoerceInt(qRow.Children().ElementAt(8).Cq().Text().Split(new char[] { '/' })[1].Trim()) + release.Seeders;

releases.Add(release);
}
}
catch (Exception ex)
{
OnParseError(results.Content, ex);
}

return releases;
}
}
}
6 changes: 5 additions & 1 deletion src/Jackett/Jackett.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
<Compile Include="Indexers\Demonoid.cs" />
<Compile Include="Indexers\FrenchTorrentDb.cs" />
<Compile Include="Indexers\BroadcastTheNet.cs" />
<Compile Include="Indexers\GFTracker.cs" />
<Compile Include="Indexers\TehConnection.cs" />
<Compile Include="Indexers\Hounddawgs.cs" />
<Compile Include="Indexers\Shazbat.cs" />
Expand Down Expand Up @@ -449,6 +450,9 @@
<Content Include="Content\logos\frenchtorrentdb.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Content\logos\gftracker.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Content\logos\hdspace.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand Down Expand Up @@ -635,4 +639,4 @@
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
</Project>
</Project>