diff --git a/src/Jackett/Indexers/BitMeTV.cs b/src/Jackett/Indexers/BitMeTV.cs index 947c06216..c7e436530 100644 --- a/src/Jackett/Indexers/BitMeTV.cs +++ b/src/Jackett/Indexers/BitMeTV.cs @@ -63,7 +63,7 @@ namespace Jackett public string DisplayName { get { return "BitMeTV"; } } public string DisplayDescription { get { return "TV Episode specialty tracker"; } } - public Uri SiteLink { get { return new Uri("https://bitmetv.org"); } } + public Uri SiteLink { get { return new Uri(BaseUrl); } } public bool IsConfigured { get; private set; } diff --git a/src/Jackett/Indexers/Freshon.cs b/src/Jackett/Indexers/Freshon.cs index a9b75c52b..0298e2c56 100644 --- a/src/Jackett/Indexers/Freshon.cs +++ b/src/Jackett/Indexers/Freshon.cs @@ -34,7 +34,7 @@ namespace Jackett public string DisplayDescription { get { return "Our goal is to provide the latest stuff in the TV show domain"; } } - public Uri SiteLink { get { return new Uri("https://freshon.tv/"); } } + public Uri SiteLink { get { return new Uri(BaseUrl); } } public event Action OnSaveConfigurationRequested; @@ -169,7 +169,7 @@ namespace Jackett else if (dateString.StartsWith("Yesterday ")) pubDate = (DateTime.UtcNow + TimeSpan.Parse(dateString.Split(' ')[1]) - TimeSpan.FromDays(1)).ToLocalTime(); else - pubDate = DateTime.ParseExact(dateString, "dd-MMM-yyyy HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime(); + pubDate = DateTime.ParseExact(dateString, "d-MMM-yyyy HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime(); release.PublishDate = pubDate; release.Seeders = int.Parse(qRow.Find("td.table_seeders").Text().Trim()); @@ -187,9 +187,12 @@ namespace Jackett return releases.ToArray(); } - public Task Download(Uri link) + public async Task Download(Uri link) { - throw new NotImplementedException(); + var request = CreateHttpRequest(link); + var response = await client.SendAsync(request); + var bytes = await response.Content.ReadAsByteArrayAsync(); + return bytes; } } } diff --git a/src/Jackett/Indexers/IPTorrents.cs b/src/Jackett/Indexers/IPTorrents.cs index 2d0f98c1a..a595907ee 100644 --- a/src/Jackett/Indexers/IPTorrents.cs +++ b/src/Jackett/Indexers/IPTorrents.cs @@ -7,6 +7,7 @@ using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; +using System.Web; namespace Jackett.Indexers { @@ -19,13 +20,17 @@ namespace Jackett.Indexers public string DisplayDescription { get { return "Always a step ahead"; } } - public Uri SiteLink { get { return new Uri("https://iptorrents.com"); } } + public Uri SiteLink { get { return new Uri(BaseUrl); } } public bool IsConfigured { get; private set; } + static string chromeUserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36"; + + static string BaseUrl = "https://iptorrents.com"; - static string chromeUserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36"; + string SearchUrl = BaseUrl + "/t?q="; + CookieContainer cookies; HttpClientHandler handler; @@ -44,71 +49,73 @@ namespace Jackett.Indexers client = new HttpClient(handler); } - public Task GetConfigurationForSetup() + public async Task GetConfigurationForSetup() { - return Task.Run(async () => - { - await client.GetAsync(new Uri(BaseUrl)); - var config = new ConfigurationDataBasicLogin(); - return (ConfigurationData)config; - }); + await client.GetAsync(new Uri(BaseUrl)); + var config = new ConfigurationDataBasicLogin(); + return (ConfigurationData)config; } - public Task ApplyConfiguration(Newtonsoft.Json.Linq.JToken configJson) + public async Task ApplyConfiguration(Newtonsoft.Json.Linq.JToken configJson) { - return Task.Run(async () => - { - var config = new ConfigurationDataBasicLogin(); - config.LoadValuesFromJson(configJson); - var pairs = new Dictionary + var config = new ConfigurationDataBasicLogin(); + config.LoadValuesFromJson(configJson); + + var pairs = new Dictionary { { "username", config.Username.Value}, { "password", config.Password.Value} }; - var content = new FormUrlEncodedContent(pairs); - var message = new HttpRequestMessage(); - message.Method = HttpMethod.Post; - message.Content = content; - message.RequestUri = new Uri(BaseUrl); - message.Headers.Referrer = new Uri(BaseUrl); - message.Headers.UserAgent.ParseAdd(chromeUserAgent); + var content = new FormUrlEncodedContent(pairs); + var message = new HttpRequestMessage(); + message.Method = HttpMethod.Post; + message.Content = content; + message.RequestUri = new Uri(BaseUrl); + message.Headers.Referrer = new Uri(BaseUrl); + message.Headers.UserAgent.ParseAdd(chromeUserAgent); - var response = await client.SendAsync(message); - var responseContent = await response.Content.ReadAsStringAsync(); + var response = await client.SendAsync(message); + var responseContent = await response.Content.ReadAsStringAsync(); - if (!responseContent.Contains("/my.php")) - { - CQ dom = responseContent; - var messageEl = dom["body > div"].First(); - var errorMessage = messageEl.Text().Trim(); - throw new ExceptionWithConfigData(errorMessage, (ConfigurationData)config); - } - else - { - var configSaveData = new JObject(); - configSaveData["cookies"] = new JArray(( - from cookie in cookies.GetCookies(new Uri(BaseUrl)).Cast() - select cookie.Name + ":" + cookie.Value - ).ToArray()); + if (!responseContent.Contains("/my.php")) + { + CQ dom = responseContent; + var messageEl = dom["body > div"].First(); + var errorMessage = messageEl.Text().Trim(); + throw new ExceptionWithConfigData(errorMessage, (ConfigurationData)config); + } + else + { + var configSaveData = new JObject(); + configSaveData["cookies"] = new JArray(( + from cookie in cookies.GetCookies(new Uri(BaseUrl)).Cast() + select cookie.Name + ":" + cookie.Value + ).ToArray()); - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); + if (OnSaveConfigurationRequested != null) + OnSaveConfigurationRequested(this, configSaveData); - IsConfigured = true; - } - }); + IsConfigured = true; + } + + } + + HttpRequestMessage CreateHttpRequest(Uri uri) + { + var message = new HttpRequestMessage(); + message.Method = HttpMethod.Get; + message.RequestUri = uri; + message.Headers.UserAgent.ParseAdd(chromeUserAgent); + return message; } public Task VerifyConnection() { return Task.Run(async () => { - var message = new HttpRequestMessage(); - message.Method = HttpMethod.Get; - message.RequestUri = new Uri(BaseUrl); - message.Headers.UserAgent.ParseAdd(chromeUserAgent); + var message = CreateHttpRequest(new Uri(BaseUrl)); var response = await client.SendAsync(message); var result = await response.Content.ReadAsStringAsync(); @@ -123,18 +130,85 @@ namespace Jackett.Indexers IsConfigured = true; } - public Task PerformQuery(TorznabQuery query) + public async Task PerformQuery(TorznabQuery query) { - return Task.Run(async () => + + List releases = new List(); + + + foreach (var title in query.ShowTitles ?? new string[] { string.Empty }) { - List releases = new List(); - return releases.ToArray(); - }); + + var searchString = title + " " + query.GetEpisodeSearchString(); + var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString); + + var request = CreateHttpRequest(new Uri(episodeSearchUrl)); + var response = await client.SendAsync(request); + var results = await response.Content.ReadAsStringAsync(); + + CQ dom = results; + + var rows = dom["table.torrents > tbody > tr"]; + foreach (var row in rows.Skip(1)) + { + var release = new ReleaseInfo(); + + var qRow = row.Cq(); + + var qTitleLink = qRow.Find("a.t_title").First(); + release.Title = qTitleLink.Text().Trim(); + release.Description = release.Title; + release.Guid = new Uri(BaseUrl + qTitleLink.Attr("href")); + release.Comments = release.Guid; + + DateTime pubDate; + var descString = qRow.Find(".t_ctime").Text(); + var dateString = descString.Split('|').Last().Trim(); + dateString = dateString.Split(new string[] { " by " }, StringSplitOptions.None)[0]; + var dateValue = float.Parse(dateString.Split(' ')[0]); + var dateUnit = dateString.Split(' ')[1]; + if (dateUnit.Contains("minute")) + pubDate = DateTime.Now - TimeSpan.FromMinutes(dateValue); + else if (dateUnit.Contains("hour")) + pubDate = DateTime.Now - TimeSpan.FromHours(dateValue); + else if (dateUnit.Contains("day")) + pubDate = DateTime.Now - TimeSpan.FromDays(dateValue); + else if (dateUnit.Contains("week")) + pubDate = DateTime.Now - TimeSpan.FromDays(7 * dateValue); + else if (dateUnit.Contains("month")) + pubDate = DateTime.Now - TimeSpan.FromDays(30 * dateValue); + else if (dateUnit.Contains("year")) + pubDate = DateTime.Now - TimeSpan.FromDays(365 * dateValue); + else + pubDate = DateTime.MinValue; + release.PublishDate = pubDate; + + var qLink = row.ChildElements.ElementAt(3).Cq().Children("a"); + release.Link = new Uri(BaseUrl + qLink.Attr("href")); + + var sizeStr = row.ChildElements.ElementAt(5).Cq().Text().Trim(); + var sizeVal = float.Parse(sizeStr.Split(' ')[0]); + var sizeUnit = sizeStr.Split(' ')[1]; + release.Size = ReleaseInfo.GetBytes(sizeUnit, sizeVal); + + release.Seeders = int.Parse(qRow.Find(".t_seeders").Text().Trim()); + release.Peers = int.Parse(qRow.Find(".t_leechers").Text().Trim()) + release.Seeders; + + releases.Add(release); + } + + } + + return releases.ToArray(); + } - public Task Download(Uri link) + public async Task Download(Uri link) { - throw new NotImplementedException(); + var request = CreateHttpRequest(link); + var response = await client.SendAsync(request); + var bytes = await response.Content.ReadAsByteArrayAsync(); + return bytes; } } } diff --git a/src/Jackett/Server.cs b/src/Jackett/Server.cs index d173c1fec..f460be0f3 100644 --- a/src/Jackett/Server.cs +++ b/src/Jackett/Server.cs @@ -60,96 +60,99 @@ namespace Jackett async void ProcessHttpRequest(HttpListenerContext context) { - if (webApi.HandleRequest(context)) - { - return; - } - - if (context.Request.Url.AbsolutePath.StartsWith("/api/")) - { - ProcessTorznab(context); - return; - } - - var responseBytes = Encoding.UTF8.GetBytes("Invalid request"); - await context.Response.OutputStream.WriteAsync(responseBytes, 0, responseBytes.Length); - context.Response.Close(); - } - - async void ProcessTorznab(HttpListenerContext context) - { - Exception exception; + Exception exception = null; try { - var query = HttpUtility.ParseQueryString(context.Request.Url.Query); - var inputStream = context.Request.InputStream; - var reader = new StreamReader(inputStream, context.Request.ContentEncoding); - var bytes = await reader.ReadToEndAsync(); - - var indexerId = context.Request.Url.Segments[2].TrimEnd('/').ToLower(); - var indexer = indexerManager.GetIndexer(indexerId); - - if (context.Request.Url.Segments.Length > 4 && context.Request.Url.Segments[3] == "download/") + if (await webApi.HandleRequest(context)) { - var downloadLink = Encoding.UTF8.GetString(Convert.FromBase64String((context.Request.Url.Segments[4].TrimEnd('/')))); - var downloadBytes = await indexer.Download(new Uri(downloadLink)); - await context.Response.OutputStream.WriteAsync(downloadBytes, 0, downloadBytes.Length); - context.Response.Close(); - return; + } - - var torznabQuery = TorznabQuery.FromHttpQuery(query); - - torznabQuery.ShowTitles = await sonarrApi.GetShowTitle(torznabQuery.RageID); - - var releases = await indexer.PerformQuery(torznabQuery); - - var severUrl = string.Format("{0}://{1}:{2}/", context.Request.Url.Scheme, context.Request.Url.Host, context.Request.Url.Port); - - var resultPage = new ResultPage(new ChannelInfo + else if (context.Request.Url.AbsolutePath.StartsWith("/api/")) { - Title = indexer.DisplayName, - Description = indexer.DisplayDescription, - Link = indexer.SiteLink, - ImageUrl = new Uri(severUrl + "logos/" + indexerId + ".png"), - ImageTitle = indexer.DisplayName, - ImageLink = indexer.SiteLink, - ImageDescription = indexer.DisplayName - }); - - // add Jackett proxy to download links... - foreach (var release in releases) - { - var originalLink = release.Link; - var encodedLink = Convert.ToBase64String(Encoding.UTF8.GetBytes(originalLink.ToString())) + "/download.torrent"; - var proxyLink = string.Format("{0}api/{1}/download/{2}", severUrl, indexerId, encodedLink); - release.Link = new Uri(proxyLink); + await ProcessTorznab(context); + } + else + { + var responseBytes = Encoding.UTF8.GetBytes("Invalid request"); + await context.Response.OutputStream.WriteAsync(responseBytes, 0, responseBytes.Length); } - - resultPage.Releases.AddRange(releases); - - var xml = resultPage.ToXml(new Uri(severUrl)); - - var responseBytes = Encoding.UTF8.GetBytes(xml); - context.Response.ContentEncoding = Encoding.UTF8; - context.Response.ContentLength64 = responseBytes.LongLength; - context.Response.ContentType = "application/rss+xml"; - await context.Response.OutputStream.WriteAsync(responseBytes, 0, responseBytes.Length); - context.Response.Close(); - return; } catch (Exception ex) { exception = ex; } - try + if (exception != null) { - var errorBytes = Encoding.UTF8.GetBytes(exception.Message); - await context.Response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length); - context.Response.Close(); + try + { + var errorBytes = Encoding.UTF8.GetBytes(exception.Message); + await context.Response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length); + } + catch (Exception) { } } - catch (Exception ex) { } + + context.Response.Close(); + + } + + async Task ProcessTorznab(HttpListenerContext context) + { + + var query = HttpUtility.ParseQueryString(context.Request.Url.Query); + var inputStream = context.Request.InputStream; + var reader = new StreamReader(inputStream, context.Request.ContentEncoding); + var bytes = await reader.ReadToEndAsync(); + + var indexerId = context.Request.Url.Segments[2].TrimEnd('/').ToLower(); + var indexer = indexerManager.GetIndexer(indexerId); + + if (context.Request.Url.Segments.Length > 4 && context.Request.Url.Segments[3] == "download/") + { + var downloadLink = Encoding.UTF8.GetString(Convert.FromBase64String((context.Request.Url.Segments[4].TrimEnd('/')))); + var downloadBytes = await indexer.Download(new Uri(downloadLink)); + await context.Response.OutputStream.WriteAsync(downloadBytes, 0, downloadBytes.Length); + return; + } + + var torznabQuery = TorznabQuery.FromHttpQuery(query); + + torznabQuery.ShowTitles = await sonarrApi.GetShowTitle(torznabQuery.RageID); + + var releases = await indexer.PerformQuery(torznabQuery); + + var severUrl = string.Format("{0}://{1}:{2}/", context.Request.Url.Scheme, context.Request.Url.Host, context.Request.Url.Port); + + var resultPage = new ResultPage(new ChannelInfo + { + Title = indexer.DisplayName, + Description = indexer.DisplayDescription, + Link = indexer.SiteLink, + ImageUrl = new Uri(severUrl + "logos/" + indexerId + ".png"), + ImageTitle = indexer.DisplayName, + ImageLink = indexer.SiteLink, + ImageDescription = indexer.DisplayName + }); + + // add Jackett proxy to download links... + foreach (var release in releases) + { + var originalLink = release.Link; + var encodedLink = Convert.ToBase64String(Encoding.UTF8.GetBytes(originalLink.ToString())) + "/download.torrent"; + var proxyLink = string.Format("{0}api/{1}/download/{2}", severUrl, indexerId, encodedLink); + release.Link = new Uri(proxyLink); + } + + resultPage.Releases.AddRange(releases); + + var xml = resultPage.ToXml(new Uri(severUrl)); + + var responseBytes = Encoding.UTF8.GetBytes(xml); + context.Response.ContentEncoding = Encoding.UTF8; + context.Response.ContentLength64 = responseBytes.LongLength; + context.Response.ContentType = "application/rss+xml"; + await context.Response.OutputStream.WriteAsync(responseBytes, 0, responseBytes.Length); + } diff --git a/src/Jackett/WebApi.cs b/src/Jackett/WebApi.cs index fc3a0e8d9..09f9d2016 100644 --- a/src/Jackett/WebApi.cs +++ b/src/Jackett/WebApi.cs @@ -48,7 +48,7 @@ namespace Jackett this.sonarrApi = sonarrApi; } - public bool HandleRequest(HttpListenerContext context) + public async Task HandleRequest(HttpListenerContext context) { string path = context.Request.Url.AbsolutePath.TrimStart('/'); if (path == "") @@ -57,21 +57,21 @@ namespace Jackett var sysPath = Path.Combine(WebContentFolder, path.Replace("/", Path.DirectorySeparatorChar.ToString())); if (Array.IndexOf(StaticFiles, sysPath) > -1) { - ServeStaticFile(context, path); + await ServeStaticFile(context, path); return true; } WebApi.WebApiMethod apiMethod; if (WebApi.WebApiMethods.TryGetValue(path, out apiMethod)) { - ProcessWebApiRequest(context, apiMethod); + await ProcessWebApiRequest(context, apiMethod); return true; } return false; } - async void ServeStaticFile(HttpListenerContext context, string file) + async Task ServeStaticFile(HttpListenerContext context, string file) { var contentFile = File.ReadAllBytes(Path.Combine(WebContentFolder, file)); context.Response.ContentType = MimeMapping.GetMimeMapping(file); @@ -79,7 +79,6 @@ namespace Jackett try { await context.Response.OutputStream.WriteAsync(contentFile, 0, contentFile.Length); - context.Response.OutputStream.Close(); } catch (HttpListenerException) { } } @@ -92,7 +91,7 @@ namespace Jackett delegate Task HandlerTask(HttpListenerContext context); - async void ProcessWebApiRequest(HttpListenerContext context, WebApiMethod method) + async Task ProcessWebApiRequest(HttpListenerContext context, WebApiMethod method) { var query = HttpUtility.ParseQueryString(context.Request.Url.Query); @@ -139,7 +138,6 @@ namespace Jackett { byte[] jsonBytes = Encoding.UTF8.GetBytes(json.ToString()); await context.Response.OutputStream.WriteAsync(jsonBytes, 0, jsonBytes.Length); - context.Response.OutputStream.Close(); } async Task HandleTestSonarr(HttpListenerContext context)