mirror of
https://github.com/plexguide/Huntarr.io.git
synced 2026-02-09 07:39:18 -06:00
update
This commit is contained in:
@@ -500,14 +500,18 @@ def get_tag_id_by_label(api_url: str, api_key: str, api_timeout: int, tag_label:
|
||||
|
||||
|
||||
def get_exempt_tag_ids(api_url: str, api_key: str, api_timeout: int, exempt_tag_labels: list) -> dict:
|
||||
"""Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label. Exact match. Issue #676."""
|
||||
if not exempt_tag_labels:
|
||||
"""Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label. Exact match. Issue #676.
|
||||
Only counts tags that are actually entered (non-empty); empty list or all-whitespace = no exclusions.
|
||||
"""
|
||||
if exempt_tag_labels is None:
|
||||
return {}
|
||||
if isinstance(exempt_tag_labels, str):
|
||||
exempt_tag_labels = [exempt_tag_labels]
|
||||
labels = [str(l).strip() for l in (exempt_tag_labels or []) if l is not None and str(l).strip()]
|
||||
if not labels:
|
||||
return {}
|
||||
result = {}
|
||||
for label in exempt_tag_labels:
|
||||
label = (label or "").strip()
|
||||
if not label:
|
||||
continue
|
||||
for label in labels:
|
||||
tid = get_tag_id_by_label(api_url, api_key, api_timeout, label)
|
||||
if tid is not None:
|
||||
result[tid] = label
|
||||
|
||||
@@ -516,14 +516,18 @@ def get_tag_id_by_label(api_url: str, api_key: str, api_timeout: int, tag_label:
|
||||
|
||||
|
||||
def get_exempt_tag_ids(api_url: str, api_key: str, api_timeout: int, exempt_tag_labels: list) -> dict:
|
||||
"""Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label. Exact match. Issue #676."""
|
||||
if not exempt_tag_labels:
|
||||
"""Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label. Exact match. Issue #676.
|
||||
Only counts tags that are actually entered (non-empty); empty list or all-whitespace = no exclusions.
|
||||
"""
|
||||
if exempt_tag_labels is None:
|
||||
return {}
|
||||
if isinstance(exempt_tag_labels, str):
|
||||
exempt_tag_labels = [exempt_tag_labels]
|
||||
labels = [str(l).strip() for l in (exempt_tag_labels or []) if l is not None and str(l).strip()]
|
||||
if not labels:
|
||||
return {}
|
||||
result = {}
|
||||
for label in exempt_tag_labels:
|
||||
label = (label or "").strip()
|
||||
if not label:
|
||||
continue
|
||||
for label in labels:
|
||||
tid = get_tag_id_by_label(api_url, api_key, api_timeout, label)
|
||||
if tid is not None:
|
||||
result[tid] = label
|
||||
|
||||
@@ -479,14 +479,17 @@ def get_exempt_tag_ids(api_url: str, api_key: str, api_timeout: int, exempt_tag_
|
||||
"""
|
||||
Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label for tags that exist.
|
||||
Exact match on label. Used to filter out movies with exempt tags (issue #676).
|
||||
Only counts tags that are actually entered (non-empty); empty list or all-whitespace = no exclusions.
|
||||
"""
|
||||
if not exempt_tag_labels:
|
||||
if exempt_tag_labels is None:
|
||||
return {}
|
||||
if isinstance(exempt_tag_labels, str):
|
||||
exempt_tag_labels = [exempt_tag_labels]
|
||||
labels = [str(l).strip() for l in (exempt_tag_labels or []) if l is not None and str(l).strip()]
|
||||
if not labels:
|
||||
return {}
|
||||
result = {}
|
||||
for label in exempt_tag_labels:
|
||||
label = (label or "").strip()
|
||||
if not label:
|
||||
continue
|
||||
for label in labels:
|
||||
tid = get_tag_id_by_label(api_url, api_key, api_timeout, label)
|
||||
if tid is not None:
|
||||
result[tid] = label
|
||||
|
||||
@@ -599,14 +599,18 @@ def get_tag_id_by_label(api_url: str, api_key: str, api_timeout: int, tag_label:
|
||||
|
||||
|
||||
def get_exempt_tag_ids(api_url: str, api_key: str, api_timeout: int, exempt_tag_labels: list) -> dict:
|
||||
"""Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label. Exact match. Issue #676."""
|
||||
if not exempt_tag_labels:
|
||||
"""Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label. Exact match. Issue #676.
|
||||
Only counts tags that are actually entered (non-empty); empty list or all-whitespace = no exclusions.
|
||||
"""
|
||||
if exempt_tag_labels is None:
|
||||
return {}
|
||||
if isinstance(exempt_tag_labels, str):
|
||||
exempt_tag_labels = [exempt_tag_labels]
|
||||
labels = [str(l).strip() for l in (exempt_tag_labels or []) if l is not None and str(l).strip()]
|
||||
if not labels:
|
||||
return {}
|
||||
result = {}
|
||||
for label in exempt_tag_labels:
|
||||
label = (label or "").strip()
|
||||
if not label:
|
||||
continue
|
||||
for label in labels:
|
||||
tid = get_tag_id_by_label(api_url, api_key, api_timeout, label)
|
||||
if tid is not None:
|
||||
result[tid] = label
|
||||
|
||||
@@ -281,6 +281,7 @@ def get_missing_episodes(api_url: str, api_key: str, api_timeout: int, monitored
|
||||
while True:
|
||||
retry_count = 0
|
||||
success = False
|
||||
records = [] # Ensure defined for "if not records" after inner loop (e.g. on request exception)
|
||||
|
||||
while retry_count <= retries_per_page and not success:
|
||||
# Parameters for the request
|
||||
@@ -288,7 +289,7 @@ def get_missing_episodes(api_url: str, api_key: str, api_timeout: int, monitored
|
||||
"page": page,
|
||||
"pageSize": page_size,
|
||||
"includeSeries": "true",
|
||||
"monitored": monitored_only
|
||||
"monitored": str(monitored_only).lower() # Sonarr API expects "true"/"false"
|
||||
}
|
||||
|
||||
# Add series ID filter if provided
|
||||
@@ -650,9 +651,10 @@ def get_missing_episodes_random_page(api_url: str, api_key: str, api_timeout: in
|
||||
"page": 1,
|
||||
"pageSize": 1,
|
||||
"includeSeries": "true", # Include series info for filtering
|
||||
"monitored": monitored_only
|
||||
"monitored": str(monitored_only).lower() # Sonarr API expects "true"/"false"
|
||||
}
|
||||
url = f"{api_url}/api/v3/{endpoint}"
|
||||
base_url = api_url.rstrip('/')
|
||||
url = f"{base_url}/api/v3/{endpoint.lstrip('/')}"
|
||||
|
||||
for attempt in range(retries + 1):
|
||||
try:
|
||||
@@ -670,14 +672,17 @@ def get_missing_episodes_random_page(api_url: str, api_key: str, api_timeout: in
|
||||
|
||||
try:
|
||||
data = response.json()
|
||||
total_records = data.get('totalRecords', 0)
|
||||
# Support both totalRecords (Sonarr v3) and total (some versions)
|
||||
total_records = data.get('totalRecords', data.get('total', 0))
|
||||
if isinstance(total_records, dict):
|
||||
total_records = 0
|
||||
|
||||
if total_records == 0:
|
||||
sonarr_logger.info("No missing episodes found in Sonarr.")
|
||||
return []
|
||||
|
||||
# Calculate total pages with our desired page size
|
||||
total_pages = (total_records + page_size - 1) // page_size
|
||||
total_pages = max(1, (total_records + page_size - 1) // page_size)
|
||||
sonarr_logger.info(f"Found {total_records} total missing episodes across {total_pages} pages")
|
||||
|
||||
if total_pages == 0:
|
||||
@@ -693,7 +698,7 @@ def get_missing_episodes_random_page(api_url: str, api_key: str, api_timeout: in
|
||||
"page": random_page,
|
||||
"pageSize": page_size,
|
||||
"includeSeries": "true",
|
||||
"monitored": monitored_only
|
||||
"monitored": str(monitored_only).lower() # Sonarr API expects "true"/"false"
|
||||
}
|
||||
|
||||
if series_id is not None:
|
||||
@@ -1180,14 +1185,19 @@ def get_tag_id_by_label(api_url: str, api_key: str, api_timeout: int, tag_label:
|
||||
|
||||
|
||||
def get_exempt_tag_ids(api_url: str, api_key: str, api_timeout: int, exempt_tag_labels: list) -> dict:
|
||||
"""Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label. Exact match. Issue #676."""
|
||||
if not exempt_tag_labels:
|
||||
"""Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label. Exact match. Issue #676.
|
||||
Only counts tags that are actually entered (non-empty); empty list or all-whitespace = no exclusions.
|
||||
"""
|
||||
if exempt_tag_labels is None:
|
||||
return {}
|
||||
# Normalize: accept list or single string; only consider non-empty stripped labels
|
||||
if isinstance(exempt_tag_labels, str):
|
||||
exempt_tag_labels = [exempt_tag_labels]
|
||||
labels = [str(l).strip() for l in (exempt_tag_labels or []) if l is not None and str(l).strip()]
|
||||
if not labels:
|
||||
return {}
|
||||
result = {}
|
||||
for label in exempt_tag_labels:
|
||||
label = (label or "").strip()
|
||||
if not label:
|
||||
continue
|
||||
for label in labels:
|
||||
tid = get_tag_id_by_label(api_url, api_key, api_timeout, label)
|
||||
if tid is not None:
|
||||
result[tid] = label
|
||||
|
||||
@@ -51,9 +51,19 @@ def should_delay_episode_search(air_date_str: str, delay_days: int) -> bool:
|
||||
# Get logger for the Sonarr app
|
||||
sonarr_logger = get_logger("sonarr")
|
||||
|
||||
|
||||
def _normalize_exempt_tags(exempt_tags: list) -> list:
|
||||
"""Only count tags that are entered (non-empty). Empty/whitespace = no exclusions. Issue #805."""
|
||||
if exempt_tags is None:
|
||||
return []
|
||||
return [t for t in (exempt_tags if isinstance(exempt_tags, list) else [exempt_tags])
|
||||
if isinstance(t, str) and (t or "").strip()]
|
||||
|
||||
|
||||
def _get_exempt_series_ids(api_url: str, api_key: str, api_timeout: int, exempt_tags: list) -> set:
|
||||
"""Return set of series IDs that have any exempt tag."""
|
||||
"""Return set of series IDs that have any exempt tag. Empty exempt list = no series excluded."""
|
||||
exempt_series_ids = set()
|
||||
exempt_tags = _normalize_exempt_tags(exempt_tags)
|
||||
if not exempt_tags:
|
||||
return exempt_series_ids
|
||||
exempt_id_to_label = sonarr_api.get_exempt_tag_ids(api_url, api_key, api_timeout, exempt_tags)
|
||||
@@ -105,7 +115,8 @@ def process_missing_episodes(
|
||||
"shows_missing": "huntarr-shows-missing"
|
||||
}
|
||||
|
||||
exempt_tags = exempt_tags or []
|
||||
# Only count tags that are entered; empty = process every item (Issue #805)
|
||||
exempt_tags = _normalize_exempt_tags(exempt_tags or [])
|
||||
|
||||
# Handle different modes
|
||||
if hunt_missing_mode == "seasons_packs":
|
||||
@@ -163,7 +174,7 @@ def process_missing_seasons_packs_mode(
|
||||
Uses a direct episode lookup approach which is much more efficient
|
||||
"""
|
||||
processed_any = False
|
||||
exempt_tags = exempt_tags or []
|
||||
exempt_tags = _normalize_exempt_tags(exempt_tags or [])
|
||||
|
||||
# Use custom tags if provided, otherwise use defaults
|
||||
if custom_tags is None:
|
||||
@@ -392,7 +403,7 @@ def process_missing_shows_mode(
|
||||
) -> bool:
|
||||
"""Process missing episodes in show mode - gets all missing episodes for entire shows."""
|
||||
processed_any = False
|
||||
exempt_tags = exempt_tags or []
|
||||
exempt_tags = _normalize_exempt_tags(exempt_tags or [])
|
||||
|
||||
# Use custom tags if provided, otherwise use defaults
|
||||
if custom_tags is None:
|
||||
@@ -617,7 +628,7 @@ def process_missing_episodes_mode(
|
||||
which can be useful for targeting specific episodes but is not recommended for most users.
|
||||
"""
|
||||
processed_any = False
|
||||
exempt_tags = exempt_tags or []
|
||||
exempt_tags = _normalize_exempt_tags(exempt_tags or [])
|
||||
|
||||
# Use custom tags if provided, otherwise use defaults
|
||||
if custom_tags is None:
|
||||
|
||||
@@ -8,6 +8,7 @@ import random
|
||||
from typing import List, Dict, Any, Set, Callable, Union, Optional, Optional
|
||||
from src.primary.utils.logger import get_logger
|
||||
from src.primary.apps.sonarr import api as sonarr_api
|
||||
from src.primary.apps.sonarr.missing import _normalize_exempt_tags
|
||||
from src.primary.stats_manager import increment_stat, check_hourly_cap_exceeded
|
||||
from src.primary.stateful_manager import is_processed, add_processed_id
|
||||
from src.primary.utils.history_utils import log_processed_media
|
||||
@@ -135,8 +136,7 @@ def process_cutoff_upgrades(
|
||||
|
||||
sonarr_logger.info(f"Using {upgrade_mode.upper()} mode for quality upgrades")
|
||||
|
||||
# Use seasons_packs mode or episodes mode
|
||||
exempt_tags = exempt_tags or []
|
||||
# Use seasons_packs mode or episodes mode (exempt_tags already normalized above)
|
||||
if upgrade_mode == "seasons_packs":
|
||||
return process_upgrade_seasons_mode(
|
||||
api_url, api_key, instance_name, api_timeout, monitored_only,
|
||||
|
||||
@@ -455,14 +455,18 @@ def get_tag_id_by_label(api_url: str, api_key: str, api_timeout: int, tag_label:
|
||||
|
||||
|
||||
def get_exempt_tag_ids(api_url: str, api_key: str, api_timeout: int, exempt_tag_labels: list) -> dict:
|
||||
"""Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label. Exact match. Issue #676."""
|
||||
if not exempt_tag_labels:
|
||||
"""Resolve exempt tag labels to tag IDs. Returns dict tag_id -> label. Exact match. Issue #676.
|
||||
Only counts tags that are actually entered (non-empty); empty list or all-whitespace = no exclusions.
|
||||
"""
|
||||
if exempt_tag_labels is None:
|
||||
return {}
|
||||
if isinstance(exempt_tag_labels, str):
|
||||
exempt_tag_labels = [exempt_tag_labels]
|
||||
labels = [str(l).strip() for l in (exempt_tag_labels or []) if l is not None and str(l).strip()]
|
||||
if not labels:
|
||||
return {}
|
||||
result = {}
|
||||
for label in exempt_tag_labels:
|
||||
label = (label or "").strip()
|
||||
if not label:
|
||||
continue
|
||||
for label in labels:
|
||||
tid = get_tag_id_by_label(api_url, api_key, api_timeout, label)
|
||||
if tid is not None:
|
||||
result[tid] = label
|
||||
|
||||
@@ -1 +1 @@
|
||||
9.1.4
|
||||
9.1.5
|
||||
|
||||
Reference in New Issue
Block a user