diff --git a/interfaces/Config/templates/config_sorting.tmpl b/interfaces/Config/templates/config_sorting.tmpl
index 2fdd0ca..322a0a1 100644
--- a/interfaces/Config/templates/config_sorting.tmpl
+++ b/interfaces/Config/templates/config_sorting.tmpl
@@ -131,6 +131,11 @@
$T('ep-us-name') |
+ $T('Resolution'): |
+ %r |
+ 1080p |
+
+
$T('fileExt'): |
%ext |
avi |
@@ -246,6 +251,11 @@
2009 |
+ $T('Resolution'): |
+ %r |
+ 1080p |
+
+
$T('extension'): |
%ext |
avi |
@@ -408,6 +418,11 @@
2000 |
+ $T('Resolution'): |
+ %r |
+ 1080p |
+
+
$T('orgFilename'): |
%fn |
$T('sort-File') |
diff --git a/sabnzbd/constants.py b/sabnzbd/constants.py
index eab736a..0eb2c80 100644
--- a/sabnzbd/constants.py
+++ b/sabnzbd/constants.py
@@ -141,6 +141,8 @@ year_match = r"[\W]([1|2]\d{3})([^\w]|$)" # Something '(YYYY)' or '.YYYY.' or '
sample_match = r"((^|[\W_])(sample|proof))" # something-sample or something-proof
+resolution_match = r"(^|[\W_])((240|360|480|540|576|720|900|1080|1440|2160|4320)[piP])([\W_]|$)" # 576i, 720p, 1080P
+
class Status:
COMPLETED = "Completed" # PP: Job is finished
diff --git a/sabnzbd/sorting.py b/sabnzbd/sorting.py
index 908fb05..294bf0f 100644
--- a/sabnzbd/sorting.py
+++ b/sabnzbd/sorting.py
@@ -38,7 +38,7 @@ from sabnzbd.filesystem import (
sanitize_foldername,
clip_path,
)
-from sabnzbd.constants import series_match, date_match, year_match, sample_match
+from sabnzbd.constants import series_match, date_match, year_match, sample_match, resolution_match
import sabnzbd.cfg as cfg
from sabnzbd.nzbstuff import NzbObject
@@ -359,6 +359,9 @@ class SeriesSorter:
self.nzo, self.match_obj, self.original_job_name
)
+ def get_show_resolution(self):
+ self.show_info["resolution"] = get_resolution(self.original_job_name)
+
def get_values(self):
""" Collect and construct all the values needed for path replacement """
try:
@@ -374,6 +377,9 @@ class SeriesSorter:
# - Episode Name
self.get_showdescriptions()
+ # - Resolution
+ self.get_show_resolution()
+
return True
except:
@@ -422,6 +428,9 @@ class SeriesSorter:
mapping.append(("%e", self.show_info["episode_num"]))
mapping.append(("%0e", self.show_info["episode_num_alt"]))
+ # Replace resolution
+ mapping.append(("%r", self.show_info["resolution"]))
+
# Make sure unsupported %desc is removed
mapping.append(("%desc", ""))
@@ -635,6 +644,9 @@ class MovieSorter:
year = ""
self.movie_info["year"] = year
+ # Get resolution
+ self.movie_info["resolution"] = get_resolution(self.original_job_name)
+
# - Get Decades
self.movie_info["decade"], self.movie_info["decade_two"] = get_decades(year)
@@ -680,6 +692,9 @@ class MovieSorter:
# Replace year
mapping.append(("%y", self.movie_info["year"]))
+ # Replace resolution
+ mapping.append(("%r", self.movie_info["resolution"]))
+
# Replace decades
mapping.append(("%decade", self.movie_info["decade"]))
mapping.append(("%0decade", self.movie_info["decade_two"]))
@@ -854,6 +869,9 @@ class DateSorter:
# - Get Decades
self.date_info["decade"], self.date_info["decade_two"] = get_decades(self.date_info["year"])
+ # - Get resolution
+ self.date_info["resolution"] = get_resolution(self.original_job_name)
+
# - Get Title
self.date_info["ttitle"], self.date_info["ttitle_two"], self.date_info["ttitle_three"] = get_titles(
self.nzo, self.match_obj, self.original_job_name, True
@@ -899,6 +917,9 @@ class DateSorter:
mapping.append(("%year", self.date_info["year"]))
mapping.append(("%y", self.date_info["year"]))
+ # Replace resolution
+ mapping.append(("%r", self.date_info["resolution"]))
+
if self.date_info["ep_name"]:
mapping.append(("%desc", self.date_info["ep_name"]))
mapping.append(("%.desc", self.date_info["ep_name_two"]))
@@ -1129,6 +1150,16 @@ def get_decades(year):
return decade, decade2
+def get_resolution(job_name):
+ try:
+ RE_RESOLUTION = re.compile(resolution_match)
+ # Use the last match, lowercased
+ resolution = RE_RESOLUTION.findall(job_name)[-1][1].lower()
+ except Exception:
+ resolution = ""
+ return resolution
+
+
def check_for_folder(path):
""" Return True if any folder is found in the tree at 'path' """
for _root, dirs, _files in os.walk(path):
diff --git a/tests/test_sorting.py b/tests/test_sorting.py
new file mode 100644
index 0000000..a5086c9
--- /dev/null
+++ b/tests/test_sorting.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python3 -OO
+# Copyright 2007-2021 The SABnzbd-Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+"""
+tests.test_sorting - Testing functions in sorting.py
+"""
+
+from sabnzbd import sorting
+from tests.testhelper import *
+
+
+class TestSorting:
+ @pytest.mark.parametrize(
+ "job_name, result",
+ [
+ ("Ubuntu.optimized.for.1080p.Screens-Canonical", "1080p"),
+ ("Debian_for_240i_Scientific_Calculators-FTPMasters", "240i"),
+ ("OpenBSD Streaming Edition 4320P", "4320p"), # Lower-case result
+ ("Surely.1080p.is.better.than.720p", "720p"), # Last hit wins
+ ("2160p.Campaign.Video", "2160p"), # Resolution at the start
+ ("Some.Linux.Iso.1234p", ""), # Non-standard resolution
+ ("No.Resolution.Anywhere", ""),
+ ("not.keeping.its1080p.distance", ""), # No separation
+ ("not_keeping_1440idistance_either", ""),
+ ("240 is a semiperfect and highly composite number", ""), # Number only
+ (480, ""),
+ (None, ""),
+ ("", ""),
+ ],
+ )
+ def test_get_resolution(self, job_name, result):
+ assert sorting.get_resolution(job_name) == result