2017-10-29 06:21:18 +00:00
|
|
|
|
using System;
|
2015-04-13 06:25:21 +00:00
|
|
|
|
using System.Collections.Generic;
|
2015-04-17 02:57:01 +00:00
|
|
|
|
using System.Globalization;
|
2015-04-13 06:25:21 +00:00
|
|
|
|
using System.Linq;
|
2017-11-05 09:42:03 +00:00
|
|
|
|
using System.Net;
|
2015-04-13 06:25:21 +00:00
|
|
|
|
using System.Text;
|
2015-04-17 02:57:01 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
2015-04-13 06:25:21 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2015-04-17 02:57:01 +00:00
|
|
|
|
using System.Web;
|
2017-10-29 06:21:18 +00:00
|
|
|
|
using CsQuery;
|
|
|
|
|
using Jackett.Models;
|
|
|
|
|
using Jackett.Models.IndexerConfig;
|
2017-10-29 10:19:09 +00:00
|
|
|
|
using Jackett.Services.Interfaces;
|
2017-10-29 06:21:18 +00:00
|
|
|
|
using Jackett.Utils;
|
|
|
|
|
using Jackett.Utils.Clients;
|
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
using NLog;
|
2015-04-13 06:25:21 +00:00
|
|
|
|
|
2015-07-19 13:22:50 +00:00
|
|
|
|
namespace Jackett.Indexers
|
2015-04-13 06:25:21 +00:00
|
|
|
|
{
|
2017-07-10 20:58:44 +00:00
|
|
|
|
public class BitMeTV : BaseWebIndexer
|
2015-04-26 06:51:52 +00:00
|
|
|
|
{
|
2015-10-05 10:22:01 +00:00
|
|
|
|
//https is poorly implemented on BitMeTV. Site uses http to login, but then redirects to https for search
|
2015-07-27 00:03:51 +00:00
|
|
|
|
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
2017-10-29 06:21:18 +00:00
|
|
|
|
|
2015-07-27 00:03:51 +00:00
|
|
|
|
private string LoginPost { get { return SiteLink + "takelogin.php"; } }
|
|
|
|
|
private string CaptchaUrl { get { return SiteLink + "visual.php"; } }
|
2015-10-05 10:22:01 +00:00
|
|
|
|
private string SearchUrl { get { return "https://www.bitmetv.org/browse.php"; } }
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2017-10-29 06:21:18 +00:00
|
|
|
|
private new ConfigurationDataCaptchaLogin configData
|
2015-08-03 21:38:45 +00:00
|
|
|
|
{
|
|
|
|
|
get { return (ConfigurationDataCaptchaLogin)base.configData; }
|
|
|
|
|
set { base.configData = value; }
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-05 09:42:03 +00:00
|
|
|
|
public BitMeTV(IIndexerConfigurationService configService, Utils.Clients.WebClient c, Logger l, IProtectionService ps)
|
2015-07-19 23:05:30 +00:00
|
|
|
|
: base(name: "BitMeTV",
|
|
|
|
|
description: "TV Episode specialty tracker",
|
2015-07-27 00:03:51 +00:00
|
|
|
|
link: "http://www.bitmetv.org/",
|
2015-08-11 18:48:25 +00:00
|
|
|
|
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
2017-07-10 20:58:44 +00:00
|
|
|
|
configService: configService,
|
2015-07-27 00:03:51 +00:00
|
|
|
|
client: c,
|
2015-08-03 21:38:45 +00:00
|
|
|
|
logger: l,
|
2015-08-07 19:09:13 +00:00
|
|
|
|
p: ps,
|
2016-02-07 05:39:20 +00:00
|
|
|
|
configData: new ConfigurationDataCaptchaLogin("Ensure that you have the 'Force SSL' option set to 'yes' in your profile on the BitMeTv webpage."))
|
2015-04-26 06:51:52 +00:00
|
|
|
|
{
|
2016-12-06 13:56:47 +00:00
|
|
|
|
Encoding = Encoding.GetEncoding("iso-8859-1");
|
2016-12-09 17:20:58 +00:00
|
|
|
|
Language = "en-us";
|
2017-01-27 15:57:32 +00:00
|
|
|
|
Type = "private";
|
2015-04-26 06:51:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-03 21:38:45 +00:00
|
|
|
|
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
2015-04-26 06:51:52 +00:00
|
|
|
|
{
|
2015-07-27 00:03:51 +00:00
|
|
|
|
var response = await webclient.GetString(new Utils.Clients.WebRequest()
|
|
|
|
|
{
|
|
|
|
|
Url = LoginUrl
|
|
|
|
|
});
|
2015-08-03 21:38:45 +00:00
|
|
|
|
CookieHeader = response.Cookies;
|
2015-07-27 00:03:51 +00:00
|
|
|
|
var captchaImage = await RequestBytesWithCookies(CaptchaUrl);
|
2015-08-03 21:38:45 +00:00
|
|
|
|
configData.CaptchaImage.Value = captchaImage.Content;
|
|
|
|
|
configData.CaptchaCookie.Value = captchaImage.Cookies;
|
|
|
|
|
return configData;
|
2015-04-26 06:51:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-28 05:31:38 +00:00
|
|
|
|
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
2015-04-26 06:51:52 +00:00
|
|
|
|
{
|
2017-01-02 20:39:28 +00:00
|
|
|
|
LoadValuesFromJson(configJson);
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
|
|
|
|
var pairs = new Dictionary<string, string> {
|
2015-08-03 21:38:45 +00:00
|
|
|
|
{ "username", configData.Username.Value },
|
|
|
|
|
{ "password", configData.Password.Value },
|
|
|
|
|
{ "secimage", configData.CaptchaText.Value }
|
|
|
|
|
};
|
2015-04-23 06:44:21 +00:00
|
|
|
|
|
2015-08-03 21:38:45 +00:00
|
|
|
|
var response = await RequestLoginAndFollowRedirect(LoginPost, pairs, configData.CaptchaCookie.Value, true);
|
2015-08-02 17:28:59 +00:00
|
|
|
|
await ConfigureIfOK(response.Cookies, response.Content.Contains("/logout.php"), async () =>
|
2015-04-26 06:51:52 +00:00
|
|
|
|
{
|
2015-07-27 00:03:51 +00:00
|
|
|
|
CQ dom = response.Content;
|
2015-04-26 06:51:52 +00:00
|
|
|
|
var messageEl = dom["table tr > td.embedded > h2"].Last();
|
|
|
|
|
var errorMessage = messageEl.Text();
|
2015-07-27 00:03:51 +00:00
|
|
|
|
var captchaImage = await RequestBytesWithCookies(CaptchaUrl);
|
2015-08-03 21:38:45 +00:00
|
|
|
|
configData.CaptchaImage.Value = captchaImage.Content;
|
|
|
|
|
configData.CaptchaText.Value = "";
|
|
|
|
|
configData.CaptchaCookie.Value = captchaImage.Cookies;
|
|
|
|
|
throw new ExceptionWithConfigData(errorMessage, configData);
|
2015-07-27 00:03:51 +00:00
|
|
|
|
});
|
2015-08-22 20:57:13 +00:00
|
|
|
|
return IndexerConfigurationStatus.RequiresTesting;
|
2015-04-26 06:51:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-07-03 05:15:47 +00:00
|
|
|
|
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
2015-04-26 06:51:52 +00:00
|
|
|
|
{
|
2015-07-28 23:10:04 +00:00
|
|
|
|
var releases = new List<ReleaseInfo>();
|
2017-11-05 09:42:03 +00:00
|
|
|
|
var episodeSearchUrl = string.Format("{0}?search={1}&cat=0&incldead=1", SearchUrl, WebUtility.UrlEncode(query.GetQueryString()));
|
2015-07-29 17:47:51 +00:00
|
|
|
|
var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
2015-07-18 20:35:02 +00:00
|
|
|
|
try
|
2015-04-26 06:51:52 +00:00
|
|
|
|
{
|
2015-07-27 00:03:51 +00:00
|
|
|
|
CQ dom = results.Content;
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2015-07-18 20:35:02 +00:00
|
|
|
|
var table = dom["tbody > tr > .latest"].Parent().Parent();
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2015-07-18 20:35:02 +00:00
|
|
|
|
foreach (var row in table.Children().Skip(1))
|
|
|
|
|
{
|
|
|
|
|
var release = new ReleaseInfo();
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2015-07-18 20:35:02 +00:00
|
|
|
|
CQ qDetailsCol = row.ChildElements.ElementAt(1).Cq();
|
|
|
|
|
CQ qLink = qDetailsCol.Children("a").First();
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2015-07-18 20:35:02 +00:00
|
|
|
|
release.MinimumRatio = 1;
|
|
|
|
|
release.MinimumSeedTime = 172800;
|
2015-07-19 17:18:54 +00:00
|
|
|
|
release.Comments = new Uri(SiteLink + "/" + qLink.Attr("href"));
|
2015-07-18 20:35:02 +00:00
|
|
|
|
release.Guid = release.Comments;
|
|
|
|
|
release.Title = qLink.Attr("title");
|
2017-10-23 16:10:19 +00:00
|
|
|
|
if (!query.MatchQueryStringAND(release.Title))
|
|
|
|
|
continue;
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2015-07-18 20:35:02 +00:00
|
|
|
|
//"Tuesday, June 11th 2013 at 03:52:53 AM" to...
|
|
|
|
|
//"Tuesday June 11 2013 03:52:53 AM"
|
|
|
|
|
var timestamp = qDetailsCol.Children("font").Text().Trim() + " ";
|
|
|
|
|
var timeParts = new List<string>(timestamp.Replace(" at", "").Replace(",", "").Split(' '));
|
|
|
|
|
timeParts[2] = Regex.Replace(timeParts[2], "[^0-9.]", "");
|
|
|
|
|
var formattedTimeString = string.Join(" ", timeParts.ToArray()).Trim();
|
|
|
|
|
var date = DateTime.ParseExact(formattedTimeString, "dddd MMMM d yyyy hh:mm:ss tt", CultureInfo.InvariantCulture);
|
|
|
|
|
release.PublishDate = DateTime.SpecifyKind(date, DateTimeKind.Utc).ToLocalTime();
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2015-10-14 10:09:00 +00:00
|
|
|
|
release.Link = new Uri(SiteLink.Replace("http:", "https:") + "/" + row.ChildElements.ElementAt(2).Cq().Children("a.index").Attr("href"));
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2015-07-26 03:53:53 +00:00
|
|
|
|
var sizeStr = row.ChildElements.ElementAt(6).Cq().Text();
|
|
|
|
|
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2015-07-18 20:35:02 +00:00
|
|
|
|
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text());
|
|
|
|
|
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(9).Cq().Text()) + release.Seeders;
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2015-07-18 20:35:02 +00:00
|
|
|
|
//if (!release.Title.ToLower().Contains(title.ToLower()))
|
|
|
|
|
// continue;
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2016-10-27 07:35:31 +00:00
|
|
|
|
var files = row.Cq().Find("td:nth-child(4)").Text();
|
|
|
|
|
release.Files = ParseUtil.CoerceInt(files);
|
|
|
|
|
|
|
|
|
|
var grabs = row.Cq().Find("td:nth-child(8)").Get(0).FirstChild.ToString();
|
|
|
|
|
release.Grabs = ParseUtil.CoerceInt(grabs);
|
|
|
|
|
|
2017-04-15 08:45:10 +00:00
|
|
|
|
release.DownloadVolumeFactor = 1;
|
2016-10-27 07:35:31 +00:00
|
|
|
|
release.UploadVolumeFactor = 1;
|
|
|
|
|
|
2015-07-18 20:35:02 +00:00
|
|
|
|
releases.Add(release);
|
2015-04-26 06:51:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-18 20:35:02 +00:00
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2015-07-27 00:03:51 +00:00
|
|
|
|
OnParseError(results.Content, ex);
|
2015-07-18 20:35:02 +00:00
|
|
|
|
}
|
2015-04-26 06:51:52 +00:00
|
|
|
|
|
2015-07-28 23:10:04 +00:00
|
|
|
|
return releases;
|
2015-04-26 06:51:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-04-13 06:25:21 +00:00
|
|
|
|
}
|