Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions autoremovetorrents/filter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#-*- coding:utf-8 -*-
from .category import CategoryFilter
from .status import StatusFilter
from .tracker import TrackerFilter
from .ratio import RatioFilter

__all__ = [
'CategoryFilter',
'StatusFilter',
'TrackerFilter',
'RatioFilter'
]
48 changes: 48 additions & 0 deletions autoremovetorrents/filter/ratio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#-*- coding:utf-8 -*-

from .filter import Filter
from .. import logger

class RatioFilter(Filter):
def __init__(self, min_ratio=None, max_ratio=None):
super(RatioFilter, self).__init__(all_seeds=None, ac=None, re=None)
# ADDED: Initialize logger
self._logger = logger.Logger.register(__name__)
# Convert to float, handling None by setting to 0.0 for min and inf for max
self._min_ratio = float(min_ratio) if min_ratio is not None else 0.0
self._max_ratio = float(max_ratio) if max_ratio is not None else float('inf')

# It's a good practice to ensure min_ratio is not greater than max_ratio.
# If min_ratio > max_ratio, it would result in no torrents passing the filter.
if self._min_ratio > self._max_ratio:
self._logger.warning(
f"min_ratio ({self._min_ratio}) is greater than max_ratio ({self._max_ratio}). "
f"This will result in no torrents passing this filter."
)

def apply(self, torrents):
# If min_ratio is at its effective minimum (0.0) and max_ratio is at its effective maximum (infinity),
# it means no specific filtering range is specified by the user for ratios,
# so all torrents pass this filter.
if self._min_ratio == 0.0 and self._max_ratio == float('inf'):
return set(torrents)

filtered_torrents = set()
for torrent in torrents:
ratio = torrent.ratio # Assuming torrent.ratio provides the numerical ratio

effective_ratio = 0.0 # Default for problematic or non-numeric ratios
if isinstance(ratio, (int, float)):
if ratio < 0: # Handle special values like -1 (e.g., qBittorrent's infinity)
effective_ratio = float('inf')
else:
effective_ratio = float(ratio) # Ensure it's a float for comparison
else:
# For non-numeric ratios, effective_ratio remains 0.0.
torrent_name = getattr(torrent, 'name', 'N/A') # Safely get torrent name
self._logger.warning(f"Torrent '{torrent_name}' has a non-numeric ratio: {ratio}. Treating as 0.0 for filtering.")

if self._min_ratio <= effective_ratio <= self._max_ratio:
filtered_torrents.add(torrent)
return filtered_torrents

103 changes: 65 additions & 38 deletions autoremovetorrents/strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from .filter.category import CategoryFilter
from .filter.status import StatusFilter
from .filter.tracker import TrackerFilter
from .filter.ratio import RatioFilter

class Strategy(object):
def __init__(self, name, conf):
Expand Down Expand Up @@ -62,46 +63,72 @@ def _apply_filters(self):
{'all':self._all_categories, 'ac':'categories', 're':'excluded_categories'}, # Category filter
{'all':self._all_status, 'ac':'status', 're':'excluded_status'}, # Status filter
{'all':self._all_trackers, 'ac':'trackers', 're':'excluded_trackers'}, # Tracker filter
{'type': 'ratio', 'min_key': 'min_ratio', 'max_key': 'max_ratio'} # Ratio filter
]
filter_obj = [CategoryFilter, StatusFilter, TrackerFilter]
filter_obj = [CategoryFilter, StatusFilter, TrackerFilter, RatioFilter]

for i in range(0, len(filter_conf)):
# Initialize all of the filter arguments
# User can use a single line to represent one item instead of using list
accept_field = filter_conf[i]['ac']
reject_field = filter_conf[i]['re']
if accept_field not in self._conf:
self._conf[accept_field] = []
if reject_field not in self._conf:
self._conf[reject_field] = []

if not isinstance(self._conf[accept_field], list):
self._conf[accept_field] = [self._conf[accept_field]] # Make it a list
if not isinstance(self._conf[reject_field], list):
self._conf[reject_field] = [self._conf[reject_field]]

# Print debug log
self._logger.debug('Applying filter %s...' % filter_obj[i].__name__)
self._logger.debug('Filter configrations: ALL: %s; ACCEPTANCES: [%s]; REJECTIONS: [%s].' % (
filter_conf[i]['all'],
', '.join(self._conf[accept_field]),
', '.join(self._conf[reject_field])
))
self._logger.debug('INPUT: %d torrent(s) before applying the filter.' % len(self.remain_list))
for torrent in self.remain_list:
self._logger.debug(torrent)

# Apply each filter
self.remain_list = filter_obj[i](
filter_conf[i]['all'],
self._conf[accept_field],
self._conf[reject_field]
).apply(self.remain_list)

# Print debug log again
self._logger.debug('OUTPUT: %d torrent(s) after applying the filter.' % len(self.remain_list))
for torrent in self.remain_list:
self._logger.debug(torrent)
current_filter_class = filter_obj[i]
current_filter_config = filter_conf[i]

self._logger.debug('Applying filter %s...' % current_filter_class.__name__)

if current_filter_config.get('type') == 'ratio':
min_ratio_val = self._conf.get(current_filter_config['min_key'])
max_ratio_val = self._conf.get(current_filter_config['max_key'])

if min_ratio_val is not None or max_ratio_val is not None:
self._logger.debug('Filter configurations: MIN_RATIO: %s; MAX_RATIO: %s.' % (
min_ratio_val, max_ratio_val
))
self._logger.debug('INPUT: %d torrent(s) before applying the filter.' % len(self.remain_list))
for torrent in self.remain_list:
self._logger.debug(torrent)

active_filter = current_filter_class(min_ratio_val, max_ratio_val)
self.remain_list = active_filter.apply(self.remain_list)

self._logger.debug('OUTPUT: %d torrent(s) after applying the filter.' % len(self.remain_list))
for torrent in self.remain_list:
self._logger.debug(torrent)
else:
self._logger.debug('Ratio filter (%s) skipped as its configuration keys (\'%s\', \'%s\') are not found in strategy config.' % (
current_filter_class.__name__,
current_filter_config['min_key'],
current_filter_config['max_key']
))
else:
accept_field = current_filter_config['ac']
reject_field = current_filter_config['re']
if accept_field not in self._conf:
self._conf[accept_field] = []
if reject_field not in self._conf:
self._conf[reject_field] = []

if not isinstance(self._conf[accept_field], list):
self._conf[accept_field] = [self._conf[accept_field]]
if not isinstance(self._conf[reject_field], list):
self._conf[reject_field] = [self._conf[reject_field]]

self._logger.debug('Filter configurations: ALL: %s; ACCEPTANCES: [%s]; REJECTIONS: [%s].' % (
current_filter_config['all'],
', '.join(map(str, self._conf[accept_field])),
', '.join(map(str, self._conf[reject_field]))
))
self._logger.debug('INPUT: %d torrent(s) before applying the filter.' % len(self.remain_list))
for torrent in self.remain_list:
self._logger.debug(torrent)

active_filter = current_filter_class(
current_filter_config['all'],
self._conf[accept_field],
self._conf[reject_field]
)
self.remain_list = active_filter.apply(self.remain_list)

self._logger.debug('OUTPUT: %d torrent(s) after applying the filter.' % len(self.remain_list))
for torrent in self.remain_list:
self._logger.debug(torrent)

# Apply Conditions
def _apply_conditions(self, client_status):
Expand Down Expand Up @@ -149,7 +176,7 @@ def _apply_conditions(self, client_status):
"%s. Your client may not support this property, so the condition %s does not work." % \
(str(e), conf)
)

# Output
self.remain_list = cond.remain
self.remove_list.update(cond.remove)
Expand Down
Loading