diff --git a/src/Jackett/ConfigurationDataCookie.cs b/src/Jackett/ConfigurationDataCookie.cs new file mode 100644 index 000000000..c184dfc5a --- /dev/null +++ b/src/Jackett/ConfigurationDataCookie.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett +{ + + public class ConfigurationDataCookie : ConfigurationData + { + public StringItem Cookie { get; private set; } + public DisplayItem CookieHint { get; private set; } + public DisplayItem CookieExample { get; private set; } + + public ConfigurationDataCookie() + { + Cookie = new StringItem { Name = "Cookie" }; + CookieHint = new DisplayItem( + "
  1. Login to BeyondHD in your browser
  2. Open the developer console, go the network tab
  3. Find 'cookie' in the request headers
  4. Copy & paste it to here
") + { + Name = "CookieHint" + }; + CookieExample = new DisplayItem( + "Example cookie header (usually longer than this):
PHPSESSID=8rk27odm; ipsconnect_63ad9c=1; more_stuff=etc;") + { + Name = "CookieExample" + }; + } + + public override Item[] GetItems() + { + return new Item[] { Cookie, CookieHint, CookieExample }; + } + + public string CookieHeader + { + get + { + return Cookie.Value.Trim().TrimStart(new char[] { '"' }).TrimEnd(new char[] { '"' }); + } + } + } + +} diff --git a/src/Jackett/Indexers/BeyondHD.cs b/src/Jackett/Indexers/BeyondHD.cs new file mode 100644 index 000000000..f8c4fe955 --- /dev/null +++ b/src/Jackett/Indexers/BeyondHD.cs @@ -0,0 +1,180 @@ +using CsQuery; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace Jackett.Indexers +{ + public class BeyondHD : IndexerInterface + { + public event Action OnSaveConfigurationRequested; + + public event Action OnResultParsingError; + + public string DisplayName + { + get { return "BeyondHD"; } + } + + public string DisplayDescription + { + get { return "Without BeyondHD, your HDTV is just a TV"; } + } + + public Uri SiteLink + { + get { return new Uri(BaseUrl); } + } + + public bool IsConfigured { get; private set; } + + const string BaseUrl = "https://beyondhd.me"; + const string SearchUrl = BaseUrl + "/browse.php?c40=1&c44=1&c48=1&c89=1&c46=1&c45=1&searchin=title&incldead=0&search={0}"; + const string DownloadUrl = BaseUrl + "/download.php?torrent={0}"; + + CookieContainer cookies; + HttpClientHandler handler; + HttpClient client; + + public BeyondHD() + { + IsConfigured = false; + cookies = new CookieContainer(); + handler = new HttpClientHandler + { + CookieContainer = cookies, + AllowAutoRedirect = true, + UseCookies = true, + }; + client = new HttpClient(handler); + } + + public Task GetConfigurationForSetup() + { + var config = new ConfigurationDataCookie(); + return Task.FromResult(config); + } + + public async Task ApplyConfiguration(JToken configJson) + { + var config = new ConfigurationDataCookie(); + config.LoadValuesFromJson(configJson); + + var jsonCookie = new JObject(); + jsonCookie["cookie_header"] = config.CookieHeader; + cookies.FillFromJson(new Uri(BaseUrl), jsonCookie); + + var responseContent = await client.GetStringAsync(BaseUrl); + + if (!responseContent.Contains("logout.php")) + { + CQ dom = responseContent; + throw new ExceptionWithConfigData("Invalid cookie header", (ConfigurationData)config); + } + else + { + var configSaveData = new JObject(); + cookies.DumpToJson(SiteLink, configSaveData); + + if (OnSaveConfigurationRequested != null) + OnSaveConfigurationRequested(this, configSaveData); + + IsConfigured = true; + } + + } + + public void LoadFromSavedConfiguration(JToken jsonConfig) + { + cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + IsConfigured = true; + } + + public async Task PerformQuery(TorznabQuery query) + { + List releases = new List(); + + foreach (var title in query.ShowTitles ?? new string[] { string.Empty }) + { + var searchString = title + " " + query.GetEpisodeSearchString(); + var episodeSearchUrl = string.Format(SearchUrl, HttpUtility.UrlEncode(searchString)); + var results = await client.GetStringAsync(episodeSearchUrl); + + try + { + CQ dom = results; + var rows = dom["table.torrenttable > tbody > tr.browse_color"]; + foreach (var row in rows) + { + var release = new ReleaseInfo(); + release.MinimumRatio = 1; + release.MinimumSeedTime = 172800; + + var qRow = row.Cq(); + + var qLink = row.ChildElements.ElementAt(2).FirstChild.Cq(); + release.Link = new Uri(BaseUrl + "/" + qLink.Attr("href")); + var torrentID = qLink.Attr("href").Split('=').Last(); + + var descCol = row.ChildElements.ElementAt(3); + var qCommentLink = descCol.FirstChild.Cq(); + release.Title = qCommentLink.Text(); + release.Description = release.Title; + release.Comments = new Uri(BaseUrl + "/" + qCommentLink.Attr("href")); + release.Guid = release.Comments; + + var dateStr = descCol.ChildElements.Last().Cq().Text().Split('|').Last().ToLowerInvariant().Replace("ago.", "").Trim(); + var dateParts = dateStr.Split(new char[] { ' ', ' ' }, StringSplitOptions.RemoveEmptyEntries); + var timeSpan = TimeSpan.Zero; + for (var i = 0; i < dateParts.Length / 2; i++) + { + var timeVal = ParseUtil.CoerceInt(dateParts[i * 2]); + var timeUnit = dateParts[i * 2 + 1]; + if (timeUnit.Contains("year")) + timeSpan += TimeSpan.FromDays(365 * timeVal); + else if (timeUnit.Contains("month")) + timeSpan += TimeSpan.FromDays(30 * timeVal); + else if (timeUnit.Contains("day")) + timeSpan += TimeSpan.FromDays(timeVal); + else if (timeUnit.Contains("hour")) + timeSpan += TimeSpan.FromHours(timeVal); + else if (timeUnit.Contains("min")) + timeSpan += TimeSpan.FromMinutes(timeVal); + } + release.PublishDate = DateTime.SpecifyKind(DateTime.Now - timeSpan, DateTimeKind.Local); + + var sizeEl = row.ChildElements.ElementAt(7); + var sizeVal = ParseUtil.CoerceFloat(sizeEl.ChildNodes.First().NodeValue); + var sizeUnit = sizeEl.ChildNodes.Last().NodeValue; + + release.Size = ReleaseInfo.GetBytes(sizeUnit, sizeVal); + + release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(9).Cq().Text()); + release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(10).Cq().Text()) + release.Seeders; + + releases.Add(release); + + } + } + + catch (Exception ex) + { + OnResultParsingError(this, results, ex); + throw ex; + } + } + return releases.ToArray(); + } + + public Task Download(Uri link) + { + return client.GetByteArrayAsync(link); + } + } +} diff --git a/src/Jackett/Indexers/SceneTime.cs b/src/Jackett/Indexers/SceneTime.cs index 6aab040f2..853faaffc 100644 --- a/src/Jackett/Indexers/SceneTime.cs +++ b/src/Jackett/Indexers/SceneTime.cs @@ -33,6 +33,7 @@ namespace Jackett.Indexers get { return new Uri(BaseUrl); } } + public bool IsConfigured { get; private set; } const string BaseUrl = "https://www.scenetime.com"; const string LoginUrl = BaseUrl + "/takelogin.php"; @@ -43,7 +44,6 @@ namespace Jackett.Indexers HttpClientHandler handler; HttpClient client; - public bool IsConfigured { get; private set; } public SceneTime() { diff --git a/src/Jackett/Jackett.csproj b/src/Jackett/Jackett.csproj index 8a65654b8..532f6f42f 100644 --- a/src/Jackett/Jackett.csproj +++ b/src/Jackett/Jackett.csproj @@ -87,6 +87,7 @@ + @@ -94,6 +95,7 @@ + @@ -164,6 +166,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/Jackett/WebContent/logos/beyondhd.png b/src/Jackett/WebContent/logos/beyondhd.png new file mode 100644 index 000000000..c52f810b3 Binary files /dev/null and b/src/Jackett/WebContent/logos/beyondhd.png differ