diff --git a/src/NzbDrone.Core.Test/Files/Indexers/TorrentRss/invalid/Eztv_InvalidDownloadUrl.xml b/src/NzbDrone.Core.Test/Files/Indexers/TorrentRss/invalid/Eztv_InvalidDownloadUrl.xml deleted file mode 100644 index 27317341c..000000000 --- a/src/NzbDrone.Core.Test/Files/Indexers/TorrentRss/invalid/Eztv_InvalidDownloadUrl.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - ezRSS - Latest torrent releases - 15 - http://ezrss.it/feed/ - - ezRSS - Latest torrent releases - http://ezrss.it/images/ezrssit.png - http://ezrss.it/feed/ - - The latest 30 torrent releases. - - <![CDATA[Andy McNabs Tour Of Duty Series 1 - Courage Under Fire 1x6 [DVDRIP - MVGROUP]]]> - invalid.url - - Mon, 15 Sep 2014 13:04:21 -0500 - - - http://eztv.it/forum/discuss/58438/ - http://eztv.it/ep/58438/andy-mcnabs-tour-of-duty-series-1-6of6-courage-under-fire-dvdrip-x264-mvgroup/ - - - 698999946 - AAA2038BED9EBCA2C312D1C9C3E8E024D0EB414E - - - - - \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/Files/Indexers/TorrentRss/invalid/ImmortalSeed_InvalidDownloadUrl.xml b/src/NzbDrone.Core.Test/Files/Indexers/TorrentRss/invalid/ImmortalSeed_InvalidDownloadUrl.xml index 12b49c5c5..d156d09ed 100644 --- a/src/NzbDrone.Core.Test/Files/Indexers/TorrentRss/invalid/ImmortalSeed_InvalidDownloadUrl.xml +++ b/src/NzbDrone.Core.Test/Files/Indexers/TorrentRss/invalid/ImmortalSeed_InvalidDownloadUrl.xml @@ -25,7 +25,7 @@ <br /> <div style="text-align: center;"><span id="lazyload"><span id="350237944_80b24e6a90a69f7bb9b39a6b3f99e4bb">&nbsp;</span> <a href="https://immortalseed.me/images/modpics/195.jpg" id="ts_show_preview" alt=""><img src="https://immortalseed.me/images/modpics/195.jpg" border="0" alt="" onload="TSResizeImage(this, '350237944_80b24e6a90a69f7bb9b39a6b3f99e4bb');" /></a></span></div> - invalid.url + Anonymous TV - High Definition 2015-02-06 13:32:26 diff --git a/src/NzbDrone.Core.Test/Files/Indexers/relative_urls.xml b/src/NzbDrone.Core.Test/Files/Indexers/relative_urls.xml new file mode 100644 index 000000000..eb08b2b02 --- /dev/null +++ b/src/NzbDrone.Core.Test/Files/Indexers/relative_urls.xml @@ -0,0 +1,18 @@ + + + + + + + Real.Time.With.Bill.Maher.2015.08.07.720p.HDTV.x264-BATV + details/123 + getnzb/123.nzb&i=782&r=123 + details/123#comments + Sat, 08 Aug 2015 14:27:16 +0100 + TV > HD + Real.Time.With.Bill.Maher.2015.08.07.720p.HDTV.x264-BATV + + + + + \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/IndexerTests/BasicRssParserFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/BasicRssParserFixture.cs index e17fb602f..99cfb82a6 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/BasicRssParserFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/BasicRssParserFixture.cs @@ -1,5 +1,8 @@ -using FluentAssertions; +using System.Linq; +using System.Text; +using FluentAssertions; using NUnit.Framework; +using NzbDrone.Common.Http; using NzbDrone.Core.Indexers; using NzbDrone.Core.Test.Framework; @@ -34,5 +37,25 @@ namespace NzbDrone.Core.Test.IndexerTests result.Should().Be(0); } + private IndexerResponse CreateResponse(string url, string content) + { + var httpRequest = new HttpRequest(url); + var httpResponse = new HttpResponse(httpRequest, new HttpHeader(), Encoding.UTF8.GetBytes(content)); + + return new IndexerResponse(new IndexerRequest(httpRequest), httpResponse); + } + + [Test] + public void should_handle_relative_url() + { + var xml = ReadAllText("Files", "Indexers", "relative_urls.xml"); + + var result = Subject.ParseResponse(CreateResponse("http://my.indexer.com/api?q=My+Favourite+Show", xml)); + + result.Should().HaveCount(1); + + result.First().CommentUrl.Should().Be("http://my.indexer.com/details/123#comments"); + result.First().DownloadUrl.Should().Be("http://my.indexer.com/getnzb/123.nzb&i=782&r=123"); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssSettingsDetectorFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssSettingsDetectorFixture.cs index df4753f2c..8399d436d 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssSettingsDetectorFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TorrentRssSettingsDetectorFixture.cs @@ -194,7 +194,6 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests settings.Should().NotBeNull(); } - [TestCase("TorrentRss/invalid/Eztv_InvalidDownloadUrl.xml")] [TestCase("TorrentRss/invalid/ImmortalSeed_InvalidDownloadUrl.xml")] public void should_reject_recent_feed_with_invalid_downloadurl(string rssXmlFile) { diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 4da4f578f..16d62a3be 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -403,14 +403,13 @@ Always - + Always - - Designer + Always - + Designer Always diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs index cc3829cd7..0a895530d 100644 --- a/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs +++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs @@ -47,12 +47,12 @@ namespace NzbDrone.Core.Indexers.Newznab protected override String GetInfoUrl(XElement item) { - return item.TryGetValue("comments").TrimEnd("#comments"); + return ParseUrl(item.TryGetValue("comments").TrimEnd("#comments")); } protected override String GetCommentUrl(XElement item) { - return item.TryGetValue("comments"); + return ParseUrl(item.TryGetValue("comments")); } protected override Int64 GetSize(XElement item) @@ -87,7 +87,7 @@ namespace NzbDrone.Core.Indexers.Newznab if (!Uri.IsWellFormedUriString(url, UriKind.Absolute)) { - url = item.Element("enclosure").Attribute("url").Value; + url = ParseUrl((string)item.Element("enclosure").Attribute("url")); } return url; diff --git a/src/NzbDrone.Core/Indexers/RssParser.cs b/src/NzbDrone.Core/Indexers/RssParser.cs index 985e4968d..2b60153d1 100644 --- a/src/NzbDrone.Core/Indexers/RssParser.cs +++ b/src/NzbDrone.Core/Indexers/RssParser.cs @@ -28,6 +28,8 @@ namespace NzbDrone.Core.Indexers // Parse "Size: 1.3 GB" or "1.3 GB" parts in the description element and use that as Size. public Boolean ParseSizeInDescription { get; set; } + private IndexerResponse _indexerResponse; + public RssParser() { _logger = NzbDroneLogger.GetLogger(this); @@ -35,6 +37,8 @@ namespace NzbDrone.Core.Indexers public virtual IList ParseResponse(IndexerResponse indexerResponse) { + _indexerResponse = indexerResponse; + var releases = new List(); if (!PreProcess(indexerResponse)) @@ -168,25 +172,25 @@ namespace NzbDrone.Core.Indexers { if (UseEnclosureUrl) { - return item.Element("enclosure").Attribute("url").Value; + return ParseUrl((string)item.Element("enclosure").Attribute("url")); } - return item.Element("link").Value; + return ParseUrl((string)item.Element("link")); } protected virtual string GetInfoUrl(XElement item) { if (UseGuidInfoUrl) { - return (String)item.Element("guid"); + return ParseUrl((string)item.Element("guid")); } - return String.Empty; + return null; } protected virtual string GetCommentUrl(XElement item) { - return (String)item.Element("comments"); + return ParseUrl((string)item.Element("comments")); } protected virtual long GetSize(XElement item) @@ -234,6 +238,31 @@ namespace NzbDrone.Core.Indexers return channel.Elements("item"); } + protected virtual string ParseUrl(string value) + { + if (value.IsNullOrWhiteSpace()) + { + return null; + } + + try + { + var uri = new Uri(value, UriKind.RelativeOrAbsolute); + + if (!uri.IsAbsoluteUri) + { + uri = new Uri(_indexerResponse.HttpRequest.Url, uri); + } + + return uri.AbsoluteUri; + } + catch (Exception ex) + { + _logger.DebugException(string.Format("Failed to parse Uri {0}, ignoring.", value), ex); + return null; + } + } + private static readonly Regex ParseSizeRegex = new Regex(@"(?(?:\d+,)*\d+(?:\.\d{1,2})?)\W?(?[KMG]i?B)(?![\w/])", RegexOptions.IgnoreCase | RegexOptions.Compiled); diff --git a/src/NzbDrone.Core/Indexers/TorrentRssParser.cs b/src/NzbDrone.Core/Indexers/TorrentRssParser.cs index de5ba8a71..c3b13aba9 100644 --- a/src/NzbDrone.Core/Indexers/TorrentRssParser.cs +++ b/src/NzbDrone.Core/Indexers/TorrentRssParser.cs @@ -66,7 +66,7 @@ namespace NzbDrone.Core.Indexers protected virtual String GetMagnetUrl(XElement item) { var downloadUrl = GetDownloadUrl(item); - if (downloadUrl.StartsWith("magnet:")) + if (downloadUrl.IsNotNullOrWhiteSpace() && downloadUrl.StartsWith("magnet:")) { return downloadUrl; } diff --git a/src/NzbDrone.Core/Indexers/Torznab/TorznabRssParser.cs b/src/NzbDrone.Core/Indexers/Torznab/TorznabRssParser.cs index 60a861c6c..bce7cd028 100644 --- a/src/NzbDrone.Core/Indexers/Torznab/TorznabRssParser.cs +++ b/src/NzbDrone.Core/Indexers/Torznab/TorznabRssParser.cs @@ -59,12 +59,12 @@ namespace NzbDrone.Core.Indexers.Torznab protected override String GetInfoUrl(XElement item) { - return item.TryGetValue("comments").TrimEnd("#comments"); + return ParseUrl(item.TryGetValue("comments").TrimEnd("#comments")); } protected override String GetCommentUrl(XElement item) { - return item.TryGetValue("comments"); + return ParseUrl(item.TryGetValue("comments")); } protected override Int64 GetSize(XElement item) @@ -93,7 +93,7 @@ namespace NzbDrone.Core.Indexers.Torznab if (!Uri.IsWellFormedUriString(url, UriKind.Absolute)) { - url = item.Element("enclosure").Attribute("url").Value; + url = ParseUrl((string)item.Element("enclosure").Attribute("url")); } return url;