Skip to content

Commit

Permalink
add smart show filter
Browse files Browse the repository at this point in the history
Main idea is search lots of torrent using title only. Filter this data using show_year, season_year, episode_year, season, episode, absolute_episode_number,season_name. More over filter looks for ranges in names e.g.: filter for ep 132 will return torrent 'Naruto [1-524]'. Same logic for years and seasons.
  • Loading branch information
vasilky3 committed Oct 27, 2023
1 parent fceaee5 commit 1c502a2
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 7 deletions.
4 changes: 4 additions & 0 deletions resources/language/English/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ msgctxt "#32058"
msgid "When looking for an episode, do a secondary search for season"
msgstr ""

msgctxt "#32059"
msgid "Use smart filter for shows. Slower but mach better results"
msgstr ""

msgctxt "#32100"
msgid "Filter Keywords"
msgstr ""
Expand Down
4 changes: 4 additions & 0 deletions resources/language/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ msgctxt "#32058"
msgid "When looking for an episode, do a secondary search for season"
msgstr ""

msgctxt "#32059"
msgid "Use smart filter for shows. Slower but mach better results"
msgstr ""

msgctxt "#32100"
msgid "Filter Keywords"
msgstr ""
Expand Down
1 change: 1 addition & 0 deletions resources/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<setting label="32052" id="elementum.jackett.sort_by" type="enum" lvalues="32053|32054|32055|32056" default="3" />
<setting label="32057" id="elementum.jackett.filter_exclude_no_seed" type="bool" default="true" />
<setting label="32058" id="elementum.jackett.search_season_on_episode" type="bool" default="true" />
<setting label="32059" id="elementum.jackett.use_smart_show_filter" type="bool" default="true" />

<setting label="32100" type="lsep"/>
<setting label="32101" id="elementum.jackett.filter_keywords_enabled" type="bool" default="false" />
Expand Down
3 changes: 3 additions & 0 deletions src/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ def _filter_season(self, results, season):
if s_re.search(result['name']) and not ep_re.search(result['name'])
]

def search_title(self, title, imdb_id):
return self.search_shows(title, imdb_id=imdb_id)

def search_season(self, title, season, imdb_id):
return self.search_shows(title, season=season, imdb_id=imdb_id)

Expand Down
66 changes: 66 additions & 0 deletions src/filter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# coding=utf-8
from logger import log
from utils import get_setting, UNKNOWN
import re


#
Expand Down Expand Up @@ -126,3 +127,68 @@ def release_type(results):
for result in results
if get_setting('include_release_' + result["release_type"], bool)
]


def tv_season_episode(results, season, season_name, episode, global_ep, ep_year, season_year=0, start_year=0):
# Function for sorting large amount of not accurate torrents. Checks year season and episodes.
# The filtering shouldn't be strict. I'm trying not to lose suitable torrents.
filtered = []
for res in results:
name = res["name"].lower()
# Remove resolution
name = re.sub(r"\d+p", '', name)
log.debug(f"torrent: {name}")

if season_name and season_name in name:
filtered.append(res)
continue

year_pattern = r"(?P<from>(?:19|20)\d{2})(?:\s*-\s*(?P<to>[12]\d{3}))?"
y = re.search(year_pattern, name)
if not y:
log.debug("No year")
continue
y_from = int(y.group("from") or "99999")
y_to = int(y.group("to") or "-1")
if (start_year != y_from and ep_year != y_from and season_year != y_from and
(y_from > season_year or season_year > y_to)):
log.debug(f"No suitable year: {ep_year or 'none'} || {season_year or 'none'} || {start_year or 'none'}")
continue
# Remove the year from the text
name_no_year = re.sub(year_pattern, '', name)

if f"s{season}e{episode}" in name_no_year:
filtered.append(res)
continue

season_pattern = r"\W(?P<s_flag>s|season|сезон)[\s\(\[\{]*(?P<from>\d+)(?:\s*-\s*(?P<to>\d+))?"
s = re.search(season_pattern, name_no_year)
if s:
s_from = int(s.group("from") or "99999")
s_to = int(s.group("to") or "-1")
s_flag = s.group("s_flag")
if season == s_from or (s_from <= season <= s_to): # season is suitable
filtered.append(res)
continue
elif s_flag and s_from != 1: # season is marked but not suitable. If season is first need check episodes
log.debug(f"No suitable season: {season or 'none'}")
continue
# Remove the season from the text
else:
log.debug("No season found")

if not global_ep:
continue
episode_pattern = r"(?:e?(?P<from>\d+)(?:\s*-\s*e?(?P<to>\d+)))|(?P<last>\d+)(?:\s*\+\s*\d*)?(?:\s*(из|of)\s*(?P<all>\d+))"
e = re.search(episode_pattern, name_no_year)
while e:
e_from = int(e.group("from") or "0")
e_to = int(e.group("to") or "0")
e_last = int(e.group("last") or "0")
if (e_from <= global_ep <= e_to) or global_ep <= e_last:
filtered.append(res)
break
name_no_year = re.sub(episode_pattern, '', name_no_year, 1)
e = re.search(episode_pattern, name_no_year)

return filtered
29 changes: 22 additions & 7 deletions src/jackett.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def validate_client():


def search(payload, method="general"):
log.info(f"got req from elementum:{payload}")
payload = parse_payload(method, payload)

log.debug(f"Searching with payload ({method}): f{payload}")
Expand All @@ -71,9 +72,9 @@ def search(payload, method="general"):
log.debug(f"All results: {results}")

log.info(f"Jackett returned {len(results)} results in {request_time} seconds")
except Exception as exc:
except Exception:
utils.notify(utils.translation(32703))
log.error(f"Got exeption: {traceback.format_exc()}")
log.error(f"Got exception: {traceback.format_exc()}")
finally:
p_dialog.close()
del p_dialog
Expand Down Expand Up @@ -111,10 +112,12 @@ def parse_payload(method, payload):
log.info(f"Could not determine search title, falling back to normal title: {payload['title']}")
payload["search_title"] = payload["title"]

payload['season_name'] = utils.check_season_name(payload["search_title"], payload.get('season_name', ""))

return payload


def filter_results(method, results):
def filter_results(method, results, season, season_name, episode, global_ep, ep_year, season_year=0, start_year=0):
log.debug(f"results before filtered: {results}")

if get_setting('filter_keywords_enabled', bool):
Expand Down Expand Up @@ -142,9 +145,15 @@ def filter_results(method, results):
results = filter.seed(results)
log.debug(f"filtering no seeds results: {results}")

if method == "episode" and get_setting("use_smart_show_filter", bool):
log.info(f"smart-filtering show torrents {len(results)}")
results = filter.tv_season_episode(results, season, season_name, episode, global_ep, ep_year, season_year,
start_year)
log.debug(f"smart-filtering show torrents results: {results}")

# todo maybe rating and codec

log.debug(f"Results resulted in {len(results)} results: {results}")
log.debug(f"Resulted in {len(results)} results: {results}")

return results

Expand Down Expand Up @@ -182,9 +191,13 @@ def search_jackett(p_dialog, payload, method):
elif method == 'season':
res = jackett.search_season(payload["search_title"], payload["season"], payload["imdb_id"])
elif method == 'episode':
res = jackett.search_episode(payload["search_title"], payload["season"], payload["episode"], payload["imdb_id"])
if get_setting("use_smart_show_filter", bool):
res = jackett.search_title(payload["search_title"], payload["imdb_id"])
else:
res = jackett.search_episode(payload["search_title"], payload["season"], payload["episode"],
payload["imdb_id"])
elif method == 'anime':
log.warning("jackett provider does not yet support anime search")
log.warn("jackett provider does not yet support anime search")
res = []
log.info(f"anime payload={payload}")
# client.search_query(payload["search_title"], payload["season"], payload["episode"], payload["imdb_id"])
Expand All @@ -193,7 +206,9 @@ def search_jackett(p_dialog, payload, method):

log.debug(f"{method} search returned {len(res)} results")
p_dialog.update(25, message=utils.translation(32750))
res = filter_results(method, res)
res = filter_results(method, res, payload.get('season', None), payload.get('season_name', ""),
payload.get('episode', None), payload.get('absolute_number', None), payload.get('year', None),
payload.get('season_year', None), payload.get('show_year', None))

res = jackett.async_magnet_resolve(res)

Expand Down
8 changes: 8 additions & 0 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,11 @@ def get_provider_color(provider_name):
colors[i] = f'{colors[i]:02x}'

return "FF" + "".join(colors).upper()


def check_season_name(title, season_name=""):
# make sure season name is unique. Not eq to movie title or "season". It saves from false-positive filtering.
season_name = season_name.lower()
if season_name in title or "season" in season_name:
return ""
return season_name

0 comments on commit 1c502a2

Please sign in to comment.