diff --git a/src/Jackett/IndexerInterface.cs b/src/Jackett/IndexerInterface.cs index f833eebc8..7d2b0a4f6 100644 --- a/src/Jackett/IndexerInterface.cs +++ b/src/Jackett/IndexerInterface.cs @@ -31,5 +31,7 @@ namespace Jackett // Called on startup when initializing indexers from saved configuration void LoadFromSavedConfiguration(JToken jsonConfig); + + Task PerformQuery(TorznabQuery query); } } diff --git a/src/Jackett/Indexers/BitMeTV.cs b/src/Jackett/Indexers/BitMeTV.cs index 81fcd9b3f..b31381656 100644 --- a/src/Jackett/Indexers/BitMeTV.cs +++ b/src/Jackett/Indexers/BitMeTV.cs @@ -136,5 +136,14 @@ namespace Jackett cookies.FillFromJson(new Uri(BaseUrl), (JArray)jsonConfig["cookies"]); IsConfigured = true; } + + public Task PerformQuery(TorznabQuery query) + { + return Task.Run(async () => + { + List releases = new List(); + return releases.ToArray(); + }); + } } } diff --git a/src/Jackett/Indexers/Freshon.cs b/src/Jackett/Indexers/Freshon.cs index aa306449d..a3885c3db 100644 --- a/src/Jackett/Indexers/Freshon.cs +++ b/src/Jackett/Indexers/Freshon.cs @@ -126,5 +126,14 @@ namespace Jackett cookies.FillFromJson(new Uri(BaseUrl), (JArray)jsonConfig["cookies"]); IsConfigured = true; } + + public Task PerformQuery(TorznabQuery query) + { + return Task.Run(async () => + { + List releases = new List(); + return releases.ToArray(); + }); + } } } diff --git a/src/Jackett/Indexers/IPTorrents.cs b/src/Jackett/Indexers/IPTorrents.cs index e2b253002..89335d574 100644 --- a/src/Jackett/Indexers/IPTorrents.cs +++ b/src/Jackett/Indexers/IPTorrents.cs @@ -122,5 +122,14 @@ namespace Jackett.Indexers cookies.FillFromJson(new Uri(BaseUrl), (JArray)jsonConfig["cookies"]); IsConfigured = true; } + + public Task PerformQuery(TorznabQuery query) + { + return Task.Run(async () => + { + List releases = new List(); + return releases.ToArray(); + }); + } } } diff --git a/src/Jackett/Indexers/ThePirateBay.cs b/src/Jackett/Indexers/ThePirateBay.cs index 241034ccc..0a30b8c35 100644 --- a/src/Jackett/Indexers/ThePirateBay.cs +++ b/src/Jackett/Indexers/ThePirateBay.cs @@ -1,16 +1,20 @@ -using Newtonsoft.Json.Linq; +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.Text.RegularExpressions; using System.Threading.Tasks; +using System.Web; namespace Jackett.Indexers { public class ThePirateBay : IndexerInterface { + class ThePirateBayConfig : ConfigurationData { public StringItem Url { get; private set; } @@ -36,7 +40,7 @@ namespace Jackett.Indexers public bool IsConfigured { get; private set; } - static string SearchUrl = "s/?q=test"; + static string SearchUrl = "s/?q=\"{0}\"&category=205&page=0&orderby=99"; static string BrowserUrl = "browse"; string BaseUrl; @@ -113,5 +117,56 @@ namespace Jackett.Indexers BaseUrl = (string)jsonConfig["base_url"]; IsConfigured = true; } + + public Task PerformQuery(TorznabQuery query) + { + return Task.Run(async () => + { + List releases = new List(); + + var search = BaseUrl + string.Format(SearchUrl, HttpUtility.UrlEncode("game of thrones s03e09")); + var results = await client.GetStringAsync(search); + CQ dom = results; + var descRegex = new Regex("Uploaded (?.*?)-(?.*?) (?.*?), Size (?.*?) (?.*?), ULed by"); + var rows = dom["#searchResult > tbody > tr"]; + foreach (var row in rows) + { + var release = new ReleaseInfo(); + CQ qRow = row.Cq(); + CQ qLink = qRow[".detLink"].First(); + CQ qPeerCols = qRow["td[align=\"right\"]"]; + + //Uploaded 08-02 2007, Size 47.15 MiB, ULed + var description = qRow[".detDesc"][0].ChildNodes[0].NodeValue.Trim(); + var descGroups = descRegex.Match(description).Groups; + release.PublishDate = new DateTime( + int.Parse(descGroups["year"].Value), + int.Parse(descGroups["month"].Value), + int.Parse(descGroups["day"].Value) + ); + var size = float.Parse(descGroups["size"].Value); + switch (descGroups["unit"].Value) + { + case "GiB": release.Size = ReleaseInfo.BytesFromGB(size); break; + case "MiB": release.Size = ReleaseInfo.BytesFromMB(size); break; + case "KiB": release.Size = ReleaseInfo.BytesFromKB(size); break; + } + + release.Comments = new Uri(BaseUrl + qLink.Attr("href").TrimStart('/')); + release.Guid = release.Comments; + release.Title = qLink.Text().Trim(); + release.Description = release.Title; + release.MagnetUrl = new Uri(qRow["td > a"].First().Attr("href")); + release.InfoHash = release.MagnetUrl.ToString().Split(':')[3].Split('&')[0]; + release.Seeders = int.Parse(qPeerCols.ElementAt(0).InnerText); + release.Peers = int.Parse(qPeerCols.ElementAt(1).InnerText) + release.Seeders; + release.MinimumRatio = 1; + release.MinimumSeedTime = 172800; + releases.Add(release); + } + + return releases.ToArray(); + }); + } } } diff --git a/src/Jackett/Jackett.csproj b/src/Jackett/Jackett.csproj index 0defb5532..1161b3884 100644 --- a/src/Jackett/Jackett.csproj +++ b/src/Jackett/Jackett.csproj @@ -110,7 +110,9 @@ - + + PreserveNewest + PreserveNewest diff --git a/src/Jackett/Program.cs b/src/Jackett/Program.cs index 66fdc9b95..2d0147f2a 100644 --- a/src/Jackett/Program.cs +++ b/src/Jackett/Program.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -16,6 +17,18 @@ namespace Jackett static void Main(string[] args) { + //var descRegex = new Regex("Uploaded (?.*?)-(?.*?) (?.*?), Size (?.*?) (?.*?), ULed"); + var descRegex = new Regex("Uploaded (?.*?)-(?.*?) (?.*?), Size (?.*?) (?.*?), ULed by"); + var m = descRegex.Match(("Uploaded 06-03 2013, Size 329.84 MiB, ULed by")); + List matches = new List(); + var date = m.Groups["month"].Value; + for (var i = 0; i < m.Groups.Count; i++) + { + var group = m.Groups[i]; + matches.Add(group.Value); ; + } + //Uploaded 08-02 2007, Size 47.15 MiB, ULed + //Uploaded (.*?) 2007, Size 47.15 MiB, ULed var resultPage = new ResultPage(new ChannelInfo { diff --git a/src/Jackett/ReleaseInfo.cs b/src/Jackett/ReleaseInfo.cs index 1124c7416..ae8494882 100644 --- a/src/Jackett/ReleaseInfo.cs +++ b/src/Jackett/ReleaseInfo.cs @@ -24,9 +24,25 @@ namespace Jackett public Uri ConverUrl { get; set; } public Uri BannerUrl { get; set; } public string InfoHash { get; set; } + public Uri MagnetUrl { get; set; } public double? MinimumRatio { get; set; } public long? MinimumSeedTime { get; set; } + public static long BytesFromGB(float gb) + { + return BytesFromMB(gb * 1024f); + } + + public static long BytesFromMB(float mb) + { + return BytesFromKB(mb * 1024f); + } + + public static long BytesFromKB(float kb) + { + return (long)(kb * 1024f); + } + } } diff --git a/src/Jackett/ResultPage.cs b/src/Jackett/ResultPage.cs index e56495559..5fa603202 100644 --- a/src/Jackett/ResultPage.cs +++ b/src/Jackett/ResultPage.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -36,7 +37,7 @@ namespace Jackett public string ToXml(Uri selfAtom) { - var doc = new XDocument( + var xdoc = new XDocument( new XDeclaration("1.0", "UTF-8", null), new XElement("rss", new XAttribute("version", "1.0"), @@ -58,31 +59,38 @@ namespace Jackett new XElement("title", ChannelInfo.ImageTitle), new XElement("link", ChannelInfo.ImageLink.ToString()), new XElement("description", ChannelInfo.ImageDescription) - ) - ), + ), - from r in Releases - select new XElement("item", - new XElement("title", r.Title), - new XElement("guid", r.Guid), - new XElement("link", r.Link), - new XElement("comments", r.Comments.ToString()), - new XElement("pubDate", xmlDateFormat(r.PublishDate)), - new XElement("category", r.Category), - new XElement("size", r.Size), - new XElement("description", r.Description), - new XElement("enclosure", new XAttribute("url", r.Link), new XAttribute("length", r.Size), new XAttribute("type", "application/x-bittorrent")), - getTorznabElement("rageid", r.RageID), - getTorznabElement("seeders", r.Seeders), - getTorznabElement("peers", r.Peers), - getTorznabElement("infohash", r.InfoHash), - getTorznabElement("minimumratio", r.MinimumRatio), - getTorznabElement("minimumseedtime", r.MinimumSeedTime) + + from r in Releases + select new XElement("item", + new XElement("title", r.Title), + new XElement("guid", r.Guid), + new XElement("comments", r.Comments.ToString()), + new XElement("pubDate", xmlDateFormat(r.PublishDate)), + new XElement("size", r.Size), + new XElement("description", r.Description), + r.Link == null ? null : new XElement("link", r.Link), + r.Category == null ? null : new XElement("category", r.Category), + new XElement( + "enclosure", + new XAttribute("url", r.Link ?? r.MagnetUrl), + new XAttribute("length", r.Size), + new XAttribute("type", r.Link == null ? "application/x-bittorrent;x-scheme-handler/magnet" : "application/x-bittorrent") + ), + getTorznabElement("magneturl", r.MagnetUrl), + getTorznabElement("rageid", r.RageID), + getTorznabElement("seeders", r.Seeders), + getTorznabElement("peers", r.Peers), + getTorznabElement("infohash", r.InfoHash), + getTorznabElement("minimumratio", r.MinimumRatio), + getTorznabElement("minimumseedtime", r.MinimumSeedTime) + ) ) ) ); - return doc.ToString(); + return xdoc.Declaration.ToString() + Environment.NewLine + xdoc.ToString(); } } } diff --git a/src/Jackett/Server.cs b/src/Jackett/Server.cs index 6c797711e..baed4e1c0 100644 --- a/src/Jackett/Server.cs +++ b/src/Jackett/Server.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.IO; using System.Linq; using System.Net; @@ -62,20 +63,67 @@ namespace Jackett return; } - var query = HttpUtility.ParseQueryString(context.Request.Url.Query); + if (context.Request.Url.AbsolutePath.StartsWith("/api/")) + { + ProcessTorznab(context); + return; + } - var inputStream = context.Request.InputStream; - var reader = new StreamReader(inputStream, context.Request.ContentEncoding); - var bytes = await reader.ReadToEndAsync(); - - var indexer = context.Request.Url.AbsolutePath.TrimStart('/').Replace("/api", "").ToLower(); - - var responseBytes = Encoding.UTF8.GetBytes(Properties.Resources.validator_reply); - context.Response.ContentEncoding = Encoding.UTF8; - context.Response.ContentLength64 = responseBytes.LongLength; - context.Response.ContentType = "application/rss+xml"; + 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; + 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.AbsolutePath.Replace("/api", "").TrimStart('/').ToLower(); + var indexer = indexerManager.GetIndexer(indexerId); + var torznabQuery = TorznabQuery.FromHttpQuery(query); + 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 + }); + + var releases = await indexer.PerformQuery(torznabQuery); + 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; + } + + var errorBytes = Encoding.UTF8.GetBytes(exception.Message); + await context.Response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length); + context.Response.Close(); + } + + + } } diff --git a/src/Jackett/TorznabQuery.cs b/src/Jackett/TorznabQuery.cs index 9e55d141a..375f20a0e 100644 --- a/src/Jackett/TorznabQuery.cs +++ b/src/Jackett/TorznabQuery.cs @@ -21,6 +21,7 @@ namespace Jackett public static TorznabQuery FromHttpQuery(NameValueCollection query) { + //{t=tvsearch&cat=5030%2c5040&extended=1&apikey=test&offset=0&limit=100&rid=24493&season=5&ep=1} var q = new TorznabQuery(); q.QueryType = query["t"]; @@ -29,9 +30,15 @@ namespace Jackett q.ApiKey = query["apikey"]; q.Limit = int.Parse(query["limit"]); q.Offset = int.Parse(query["offset"]); - q.RageID = int.Parse(query["rid"]); - q.Season = int.Parse(query["season"]); - q.Episode = int.Parse(query["ep"]); + + int temp; + if (int.TryParse(query["rid"], out temp)) + q.RageID = temp; + if (int.TryParse(query["season"], out temp)) + q.Season = temp; + if (int.TryParse(query["ep"], out temp)) + q.Episode = int.Parse(query["ep"]); + return q; } } diff --git a/src/Jackett/WebContent/index.html b/src/Jackett/WebContent/index.html index 479940b0b..f49d2c5e2 100644 --- a/src/Jackett/WebContent/index.html +++ b/src/Jackett/WebContent/index.html @@ -3,11 +3,13 @@ + - + + Jackett @@ -298,7 +300,7 @@ var unconfiguredIndexerTemplate = Handlebars.compile($("#templates > .unconfigured-indexer")[0].outerHTML); for (var i = 0; i < items.length; i++) { var item = items[i]; - item.torznab_host = resolveUrl("/torznab/" + item.id); + item.torznab_host = resolveUrl("/api/" + item.id); if (item.configured) $('#indexers').prepend(indexerTemplate(item)); else