Filter rss enclosures by application/x-nzb mimetype (#3093)

* Filter rss enclosures by application/x-nzb mimetype

* Add tests and fix the failing one

* Change empty RSS entry logging to info
This commit is contained in:
Michael Nightingale
2025-05-22 19:44:30 +01:00
committed by GitHub
parent 9b1b908115
commit d129eec4a7
6 changed files with 133 additions and 7 deletions

View File

@@ -649,19 +649,26 @@ def _get_link(entry):
"""Retrieve the post link from this entry
Returns (link, category, size)
"""
link = None
size = 0
age = datetime.datetime.now()
# Try standard link and enclosures first
link = entry.link
if not link:
link = entry.links[0].href
if "enclosures" in entry:
if "enclosures" in entry and entry["enclosures"]:
try:
link = entry.enclosures[0]["href"]
size = int(entry.enclosures[0]["length"])
for enclosure in entry["enclosures"]:
if "type" in enclosure and enclosure["type"] != "application/x-nzb":
continue
link = enclosure["href"]
size = int(enclosure["length"])
break
except Exception:
pass
else:
link = entry.link
if not link:
link = entry.links[0].href
# GUID usually has URL to result on page
infourl = None
@@ -717,7 +724,7 @@ def _get_link(entry):
return link, infourl, category, size, age, season, episode
else:
logging.warning(T("Empty RSS entry found (%s)"), link)
logging.info(T("Empty RSS entry found (%s)"), link)
return None, None, "", 0, None, 0, 0

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>My RSS Feed</title>
<link>https://sabnzbd.org/</link>
<item>
<title>TITLE</title>
<link>http://LINK</link>
<comments>COMMENTS</comments>
<enclosure url="http://TORRENT_LINK" type="application/x-bittorrent" length="100" />
<enclosure url="http://NZB_LINK" type="application/x-nzb" length="200" />
<pubDate>Tue, 20 May 2025 18:21:01 +0000</pubDate>
<guid isPermaLink="true">https://sabnzbd.org/rss_enclosure_multiple</guid>
</item>
</channel>
</rss>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>My RSS Feed</title>
<link>https://sabnzbd.org/</link>
<item>
<title>TITLE</title>
<link>http://LINK</link>
<comments>COMMENTS</comments>
<enclosure url="http://TORRENT_LINK" type="application/x-bittorrent" length="100" />
<pubDate>Tue, 20 May 2025 18:21:01 +0000</pubDate>
<guid isPermaLink="true">https://sabnzbd.org/rss_enclosure_no_nzb</guid>
</item>
</channel>
</rss>

15
tests/data/rss_link.xml Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>My RSS Feed</title>
<link>https://sabnzbd.org/</link>
<item>
<title>TITLE</title>
<link>http://LINK</link>
<comments>COMMENTS</comments>
<description><![CDATA[<strong>Total Size</strong>: 200.0 B]]></description>
<pubDate>Tue, 20 May 2025 18:21:01 +0000</pubDate>
<guid isPermaLink="true">https://sabnzbd.org/rss_link</guid>
</item>
</channel>
</rss>

View File

@@ -21,9 +21,11 @@ tests.test_misc - Testing functions in misc.py
import datetime
import time
import configobj
from pytest_httpserver import HTTPServer
import sabnzbd.rss as rss
import sabnzbd.config
from tests.testhelper import httpserver_handler_data_dir
class TestRSS:
@@ -97,3 +99,67 @@ class TestRSS:
# of the system, so now we have to return to UTC
adjusted_date = datetime.datetime(2019, 3, 2, 17, 18, 7) - datetime.timedelta(seconds=time.timezone)
assert job_data["age"] == adjusted_date
def test_rss_link(self, httpserver: HTTPServer):
httpserver.expect_request("/rss_link.xml").respond_with_handler(httpserver_handler_data_dir)
feed_name = "TestFeedLink"
self.setup_rss(feed_name, httpserver.url_for("/rss_link.xml"))
# Start the RSS reader
rss_obj = rss.RSSReader()
rss_obj.run_feed(feed_name)
# Is the feed processed?
assert feed_name in rss_obj.jobs
assert "http://LINK" in rss_obj.jobs[feed_name]
# Check some job-data
job_data = rss_obj.jobs[feed_name]["http://LINK"]
assert job_data["title"] == "TITLE"
assert job_data["infourl"] == "https://sabnzbd.org/rss_link"
assert job_data["size"] == 200
# feedparser returns UTC so SABnzbd converts to locale
# of the system, so now we have to return to UTC
adjusted_date = datetime.datetime(2025, 5, 20, 18, 21, 1) - datetime.timedelta(seconds=time.timezone)
assert job_data["age"] == adjusted_date
def test_rss_enclosure_no_nzb(self, httpserver: HTTPServer):
httpserver.expect_request("/rss_enclosure_no_nzb.xml").respond_with_handler(httpserver_handler_data_dir)
feed_name = "TestFeedEnclosureNoNZB"
self.setup_rss(feed_name, httpserver.url_for("/rss_enclosure_no_nzb.xml"))
# Start the RSS reader
rss_obj = rss.RSSReader()
rss_obj.run_feed(feed_name)
# Is the feed processed?
assert feed_name in rss_obj.jobs
assert not rss_obj.jobs[feed_name]
def test_rss_enclosure_multiple(self, httpserver: HTTPServer):
httpserver.expect_request("/rss_enclosure_multiple.xml").respond_with_handler(httpserver_handler_data_dir)
feed_name = "TestFeedEnclosureMultiple"
self.setup_rss(feed_name, httpserver.url_for("/rss_enclosure_multiple.xml"))
# Start the RSS reader
rss_obj = rss.RSSReader()
rss_obj.run_feed(feed_name)
# Is the feed processed?
assert feed_name in rss_obj.jobs
assert "http://NZB_LINK" in rss_obj.jobs[feed_name]
# Check some job-data
job_data = rss_obj.jobs[feed_name]["http://NZB_LINK"]
assert job_data["title"] == "TITLE"
assert job_data["infourl"] == "https://sabnzbd.org/rss_enclosure_multiple"
assert job_data["size"] == 200
# feedparser returns UTC so SABnzbd converts to locale
# of the system, so now we have to return to UTC
adjusted_date = datetime.datetime(2025, 5, 20, 18, 21, 1) - datetime.timedelta(seconds=time.timezone)
assert job_data["age"] == adjusted_date

View File

@@ -36,6 +36,8 @@ from unittest import mock
from urllib3.exceptions import ProtocolError
import xmltodict
import functools
from werkzeug import Request
from werkzeug.utils import send_from_directory
import sabnzbd
import sabnzbd.cfg as cfg
@@ -164,6 +166,11 @@ def create_and_read_nzb_fp(nzbdir: str, metadata: Optional[Dict[str, str]] = Non
return io.BytesIO(nzb_data)
def httpserver_handler_data_dir(request: Request):
"""Respond to a httpserver request with a file in SAB_DATA_DIR"""
return send_from_directory(directory=SAB_DATA_DIR, path=request.path.lstrip("/"), environ=request.environ)
def random_name(length: int = 16) -> str:
"""Shorthand to create a simple random string"""
return "".join(choice(ascii_lowercase + digits) for _ in range(length))