From dfcd5896a684cb938a4ca1948c4b1d15e32b9182 Mon Sep 17 00:00:00 2001 From: davidemarcoli Date: Wed, 6 Nov 2024 21:15:36 +0100 Subject: [PATCH] feat: enhance filesize validation for movies and episodes, improve logging and delete invalid torrents --- src/program/services/downloaders/__init__.py | 6 ++ src/program/services/downloaders/shared.py | 82 +++++++++++++------- src/program/settings/models.py | 4 +- 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/program/services/downloaders/__init__.py b/src/program/services/downloaders/__init__.py index 8b65e7a5..c80a1dc7 100644 --- a/src/program/services/downloaders/__init__.py +++ b/src/program/services/downloaders/__init__.py @@ -98,6 +98,9 @@ def update_item_attributes(self, item: MediaItem, info, container) -> bool: item.active_stream = {"infohash": info["hash"], "id": info["id"]} found = True break + else: + logger.debug(f"Deleting {info['id']} because it doesn't match the item type or size") + self.delete_torrent(info["id"]) if item.type in ["show", "season", "episode"]: show = item if item.type == "season": @@ -117,4 +120,7 @@ def update_item_attributes(self, item: MediaItem, info, container) -> bool: # We have to make sure the episode is correct if item is an episode if item.type != "episode" or (item.type == "episode" and episode.number == item.number): found = True + else: + logger.debug(f"Deleting {info['id']} because it doesn't match the item type or size") + self.delete_torrent(info["id"]) return found \ No newline at end of file diff --git a/src/program/services/downloaders/shared.py b/src/program/services/downloaders/shared.py index 28dd0a00..a5781ee6 100644 --- a/src/program/services/downloaders/shared.py +++ b/src/program/services/downloaders/shared.py @@ -1,5 +1,6 @@ from abc import ABC, abstractmethod from datetime import datetime +from typing import Tuple from loguru import logger from RTN import parse @@ -124,41 +125,68 @@ def hash_from_uri(magnet_uri: str) -> str: max_episode_filesize = settings_manager.settings.downloaders.episode_filesize_mb_max are_filesizes_valid = False -def _validate_filesizes() -> bool: - if not isinstance(settings_manager.settings.downloaders.movie_filesize_mb_min, int) or settings_manager.settings.downloaders.movie_filesize_mb_min < -1: - logger.error("Movie filesize min is not set or invalid.") - return False - if not isinstance(settings_manager.settings.downloaders.movie_filesize_mb_max, int) or settings_manager.settings.downloaders.movie_filesize_mb_max < -1: - logger.error("Movie filesize max is not set or invalid.") - return False - if not isinstance(settings_manager.settings.downloaders.episode_filesize_mb_min, int) or settings_manager.settings.downloaders.episode_filesize_mb_min < -1: - logger.error("Episode filesize min is not set or invalid.") - return False - if not isinstance(settings_manager.settings.downloaders.episode_filesize_mb_max, int) or settings_manager.settings.downloaders.episode_filesize_mb_max < -1: - logger.error("Episode filesize max is not set or invalid.") +def _validate_filesize_setting(value: int, setting_name: str) -> bool: + """Validate a single filesize setting.""" + if not isinstance(value, int) or value < -1: + logger.error(f"{setting_name} is not valid. Got {value}, expected integer >= -1") return False return True +def _validate_filesizes() -> bool: + """ + Validate all filesize settings from configuration. + Returns True if all settings are valid integers >= -1, False otherwise. + """ + settings = settings_manager.settings.downloaders + return all([ + _validate_filesize_setting(settings.movie_filesize_mb_min, "Movie filesize min"), + _validate_filesize_setting(settings.movie_filesize_mb_max, "Movie filesize max"), + _validate_filesize_setting(settings.episode_filesize_mb_min, "Episode filesize min"), + _validate_filesize_setting(settings.episode_filesize_mb_max, "Episode filesize max") + ]) + are_filesizes_valid = _validate_filesizes() -def filesize_is_acceptable_movie(filesize): +BYTES_PER_MB = 1_000_000 + +def _convert_to_bytes(size_mb: int) -> int: + """Convert size from megabytes to bytes.""" + return size_mb * BYTES_PER_MB + +def _get_size_limits(media_type: str) -> Tuple[int, int]: + """Get min and max size limits in MB for given media type.""" + settings = settings_manager.settings.downloaders + if media_type == "movie": + return (settings.movie_filesize_mb_min, settings.movie_filesize_mb_max) + return (settings.episode_filesize_mb_min, settings.episode_filesize_mb_max) + +def _validate_filesize(filesize: int, media_type: str) -> bool: + """ + Validate file size against configured limits. + + Args: + filesize: Size in bytes to validate + media_type: Type of media being validated + + Returns: + bool: True if size is within configured range + """ if not are_filesizes_valid: - logger.error("Filesize settings are invalid, movie file sizes will not be checked.") + logger.error(f"Filesize settings are invalid, {media_type.name.lower()} file sizes will not be checked.") return True - min_size = settings_manager.settings.downloaders.movie_filesize_mb_min * 1_000_000 - max_size = settings_manager.settings.downloaders.movie_filesize_mb_max * 1_000_000 if settings_manager.settings.downloaders.movie_filesize_mb_max != -1 else float("inf") + + min_mb, max_mb = _get_size_limits(media_type) + min_size = 0 if min_mb == -1 else _convert_to_bytes(min_mb) + max_size = float("inf") if max_mb == -1 else _convert_to_bytes(max_mb) + is_acceptable = min_size <= filesize <= max_size if not is_acceptable: - logger.debug(f"Filesize {filesize} is not within acceptable range {min_size} - {max_size}") + logger.debug(f"{media_type} filesize {filesize} bytes is not within acceptable range {min_size} - {max_size} bytes") return is_acceptable -def filesize_is_acceptable_show(filesize): - if not are_filesizes_valid: - logger.error("Filesize settings are invalid, episode file sizes will not be checked.") - return True - min_size = settings_manager.settings.downloaders.episode_filesize_mb_min * 1_000_000 - max_size = settings_manager.settings.downloaders.episode_filesize_mb_max * 1_000_000 if settings_manager.settings.downloaders.episode_filesize_mb_max != -1 else float("inf") - is_acceptable = min_size <= filesize <= max_size - if not is_acceptable: - logger.debug(f"Filesize {filesize} is not within acceptable range {min_size} - {max_size}") - return is_acceptable \ No newline at end of file + +def filesize_is_acceptable_movie(filesize: int) -> bool: + return _validate_filesize(filesize, "movie") + +def filesize_is_acceptable_show(filesize: int) -> bool: + return _validate_filesize(filesize, "show") \ No newline at end of file diff --git a/src/program/settings/models.py b/src/program/settings/models.py index 8773da40..f9ddb9e9 100644 --- a/src/program/settings/models.py +++ b/src/program/settings/models.py @@ -62,9 +62,9 @@ class TorboxModel(Observable): class DownloadersModel(Observable): video_extensions: List[str] = ["mp4", "mkv", "avi"] prefer_speed_over_quality: bool = True - movie_filesize_mb_min: int = -1 # MB + movie_filesize_mb_min: int = 0 # MB movie_filesize_mb_max: int = -1 # MB (-1 is no limit) - episode_filesize_mb_min: int = -1 # MB + episode_filesize_mb_min: int = 0 # MB episode_filesize_mb_max: int = -1 # MB (-1 is no limit) real_debrid: RealDebridModel = RealDebridModel() all_debrid: AllDebridModel = AllDebridModel()