Merge pull request #19 from PiratesIRC/claude/fix-stream-mapparr-channels-01HW2MuLHZwJZZvRovPCZuas

Fix missing channel handling in Stream-Mapparr
This commit is contained in:
Pirates IRC
2025-11-28 11:55:28 -06:00
committed by GitHub
2 changed files with 86 additions and 59 deletions

View File

@@ -115,7 +115,7 @@ class FuzzyMatcher:
self.logger.warning(f"No *_channels.json files found in {self.plugin_dir}") self.logger.warning(f"No *_channels.json files found in {self.plugin_dir}")
return False return False
self.logger.info(f"Found {len(channel_files)} channel database file(s): {[os.path.basename(f) for f in channel_files]}") self.logger.debug(f"Found {len(channel_files)} channel database file(s): {[os.path.basename(f) for f in channel_files]}")
total_broadcast = 0 total_broadcast = 0
total_premium = 0 total_premium = 0
@@ -158,12 +158,12 @@ class FuzzyMatcher:
total_broadcast += file_broadcast total_broadcast += file_broadcast
total_premium += file_premium total_premium += file_premium
self.logger.info(f"Loaded from {os.path.basename(channel_file)}: {file_broadcast} broadcast, {file_premium} premium channels") self.logger.debug(f"Loaded from {os.path.basename(channel_file)}: {file_broadcast} broadcast, {file_premium} premium channels")
except Exception as e: except Exception as e:
self.logger.error(f"Error loading {channel_file}: {e}") self.logger.error(f"Error loading {channel_file}: {e}")
self.logger.info(f"Total channels loaded: {total_broadcast} broadcast, {total_premium} premium") self.logger.debug(f"Total channels loaded: {total_broadcast} broadcast, {total_premium} premium")
return True return True
def reload_databases(self, country_codes=None): def reload_databases(self, country_codes=None):
@@ -205,7 +205,7 @@ class FuzzyMatcher:
self.logger.warning(f"No channel database files found to load") self.logger.warning(f"No channel database files found to load")
return False return False
self.logger.info(f"Loading {len(channel_files)} channel database file(s): {[os.path.basename(f) for f in channel_files]}") self.logger.debug(f"Loading {len(channel_files)} channel database file(s): {[os.path.basename(f) for f in channel_files]}")
total_broadcast = 0 total_broadcast = 0
total_premium = 0 total_premium = 0
@@ -248,12 +248,12 @@ class FuzzyMatcher:
total_broadcast += file_broadcast total_broadcast += file_broadcast
total_premium += file_premium total_premium += file_premium
self.logger.info(f"Loaded from {os.path.basename(channel_file)}: {file_broadcast} broadcast, {file_premium} premium channels") self.logger.debug(f"Loaded from {os.path.basename(channel_file)}: {file_broadcast} broadcast, {file_premium} premium channels")
except Exception as e: except Exception as e:
self.logger.error(f"Error loading {channel_file}: {e}") self.logger.error(f"Error loading {channel_file}: {e}")
self.logger.info(f"Total channels loaded: {total_broadcast} broadcast, {total_premium} premium") self.logger.debug(f"Total channels loaded: {total_broadcast} broadcast, {total_premium} premium")
return True return True
def extract_callsign(self, channel_name): def extract_callsign(self, channel_name):

View File

@@ -387,7 +387,7 @@ class Plugin:
if os.path.exists(self.settings_file): if os.path.exists(self.settings_file):
with open(self.settings_file, 'r') as f: with open(self.settings_file, 'r') as f:
self.saved_settings = json.load(f) self.saved_settings = json.load(f)
LOGGER.info("[Stream-Mapparr] Loaded saved settings") LOGGER.debug("[Stream-Mapparr] Loaded saved settings")
# Start background scheduler with loaded settings # Start background scheduler with loaded settings
self._start_background_scheduler(self.saved_settings) self._start_background_scheduler(self.saved_settings)
else: else:
@@ -411,7 +411,7 @@ class Plugin:
try: try:
scheduled_times_str = settings.get("scheduled_times") or "" scheduled_times_str = settings.get("scheduled_times") or ""
scheduled_times_str = scheduled_times_str.strip() if scheduled_times_str else "" scheduled_times_str = scheduled_times_str.strip() if scheduled_times_str else ""
logger.info(f"[Stream-Mapparr] Update Schedule - scheduled_times value: '{scheduled_times_str}'") logger.debug(f"[Stream-Mapparr] Update Schedule - scheduled_times value: '{scheduled_times_str}'")
# Save settings to disk # Save settings to disk
self._save_settings(settings) self._save_settings(settings)
@@ -730,7 +730,7 @@ class Plugin:
match_threshold=match_threshold, match_threshold=match_threshold,
logger=LOGGER logger=LOGGER
) )
LOGGER.info(f"[Stream-Mapparr] Initialized FuzzyMatcher with threshold: {match_threshold}") LOGGER.debug(f"[Stream-Mapparr] Initialized FuzzyMatcher with threshold: {match_threshold}")
except Exception as e: except Exception as e:
LOGGER.warning(f"[Stream-Mapparr] Failed to initialize FuzzyMatcher: {e}") LOGGER.warning(f"[Stream-Mapparr] Failed to initialize FuzzyMatcher: {e}")
self.fuzzy_matcher = None self.fuzzy_matcher = None
@@ -738,15 +738,15 @@ class Plugin:
def get_or_refresh_api_token(self, settings, logger): def get_or_refresh_api_token(self, settings, logger):
"""Get API token from cache or refresh if expired.""" """Get API token from cache or refresh if expired."""
if self.api_token and self.token_expiration and self.token_expiration > datetime.now(): if self.api_token and self.token_expiration and self.token_expiration > datetime.now():
logger.info("[Stream-Mapparr] Using cached API token.") logger.debug("[Stream-Mapparr] Using cached API token.")
return self.api_token, None return self.api_token, None
logger.info("[Stream-Mapparr] API token is expired or not found, getting a new one.") logger.debug("[Stream-Mapparr] API token is expired or not found, getting a new one.")
token, error = self._get_api_token(settings, logger) token, error = self._get_api_token(settings, logger)
if token: if token:
self.api_token = token self.api_token = token
self.token_expiration = datetime.now() + timedelta(minutes=30) self.token_expiration = datetime.now() + timedelta(minutes=30)
logger.info("[Stream-Mapparr] API token cached for 30 minutes.") logger.debug("[Stream-Mapparr] API token cached for 30 minutes.")
return token, error return token, error
@@ -764,7 +764,7 @@ class Plugin:
url = f"{dispatcharr_url}/api/accounts/token/" url = f"{dispatcharr_url}/api/accounts/token/"
payload = {"username": username, "password": password} payload = {"username": username, "password": password}
logger.info(f"[Stream-Mapparr] Attempting to authenticate with Dispatcharr at: {url}") logger.debug(f"[Stream-Mapparr] Attempting to authenticate with Dispatcharr at: {url}")
response = requests.post(url, json=payload, timeout=15) response = requests.post(url, json=payload, timeout=15)
if response.status_code == 401: if response.status_code == 401:
@@ -785,7 +785,7 @@ class Plugin:
logger.error("[Stream-Mapparr] No access token returned from API") logger.error("[Stream-Mapparr] No access token returned from API")
return None, "Login successful, but no access token was returned by the API." return None, "Login successful, but no access token was returned by the API."
logger.info("[Stream-Mapparr] Successfully obtained API access token") logger.debug("[Stream-Mapparr] Successfully obtained API access token")
return access_token, None return access_token, None
except requests.exceptions.ConnectionError as e: except requests.exceptions.ConnectionError as e:
@@ -869,7 +869,7 @@ class Plugin:
try: try:
if limiter: limiter.wait() if limiter: limiter.wait()
logger.info(f"[Stream-Mapparr] Making API PATCH request to: {endpoint}") logger.debug(f"[Stream-Mapparr] Making API PATCH request to: {endpoint}")
response = requests.patch(url, headers=headers, json=payload, timeout=60) response = requests.patch(url, headers=headers, json=payload, timeout=60)
# --- Smart Rate Limiter Logic --- # --- Smart Rate Limiter Logic ---
@@ -917,7 +917,7 @@ class Plugin:
headers = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'} headers = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'}
try: try:
logger.info(f"[Stream-Mapparr] Making API POST request to: {endpoint}") logger.debug(f"[Stream-Mapparr] Making API POST request to: {endpoint}")
response = requests.post(url, headers=headers, json=payload, timeout=30) response = requests.post(url, headers=headers, json=payload, timeout=30)
if response.status_code == 401: if response.status_code == 401:
@@ -961,7 +961,7 @@ class Plugin:
"message": "Channel visibility updated by Event Channel Managarr" "message": "Channel visibility updated by Event Channel Managarr"
} }
) )
logger.info("[Stream-Mapparr] Frontend refresh triggered via WebSocket") logger.debug("[Stream-Mapparr] Frontend refresh triggered via WebSocket")
return True return True
except Exception as e: except Exception as e:
logger.warning(f"[Stream-Mapparr] Could not trigger frontend refresh: {e}") logger.warning(f"[Stream-Mapparr] Could not trigger frontend refresh: {e}")
@@ -1160,12 +1160,13 @@ class Plugin:
continue continue
channels_data.extend(channels_list) channels_data.extend(channels_list)
logger.info(f"[Stream-Mapparr] Loaded {len(channels_list)} channels from {db_label}") logger.debug(f"[Stream-Mapparr] Loaded {len(channels_list)} channels from {db_label}")
except Exception as e: except Exception as e:
logger.error(f"[Stream-Mapparr] Error loading {channel_file}: {e}") logger.error(f"[Stream-Mapparr] Error loading {channel_file}: {e}")
logger.info(f"[Stream-Mapparr] Loaded total of {len(channels_data)} channels from {len(enabled_databases)} enabled database(s)") db_names = [db_info['label'] for db_info in enabled_databases]
logger.info(f"[Stream-Mapparr] Loaded total of {len(channels_data)} channels from {len(enabled_databases)} enabled database(s): {', '.join(db_names)}")
except Exception as e: except Exception as e:
logger.error(f"[Stream-Mapparr] Error loading channel data files: {e}") logger.error(f"[Stream-Mapparr] Error loading channel data files: {e}")
@@ -1496,7 +1497,7 @@ class Plugin:
if not is_success: if not is_success:
notification_data['error'] = message notification_data['error'] = message
LOGGER.info(f"[Stream-Mapparr] Sending notification: {action_id} ({'success' if is_success else 'error'}) - {message}") LOGGER.debug(f"[Stream-Mapparr] Sending notification: {action_id} ({'success' if is_success else 'error'}) - {message}")
send_websocket_update('updates', 'update', notification_data) send_websocket_update('updates', 'update', notification_data)
except Exception as e: except Exception as e:
@@ -1517,7 +1518,7 @@ class Plugin:
def save_settings(self, settings, context): def save_settings(self, settings, context):
"""Save settings and sync schedules automatically""" """Save settings and sync schedules automatically"""
try: try:
LOGGER.info(f"[Stream-Mapparr] Saving settings with keys: {list(settings.keys())}") LOGGER.debug(f"[Stream-Mapparr] Saving settings with keys: {list(settings.keys())}")
# Get timezone and schedule settings # Get timezone and schedule settings
user_timezone = settings.get("timezone") or "US/Central" user_timezone = settings.get("timezone") or "US/Central"
@@ -1528,7 +1529,7 @@ class Plugin:
cron_schedule = settings.get("schedule_cron") or "" cron_schedule = settings.get("schedule_cron") or ""
cron_schedule = cron_schedule.strip() if cron_schedule else "" cron_schedule = cron_schedule.strip() if cron_schedule else ""
LOGGER.info(f"[Stream-Mapparr] Schedule settings: enabled={enabled}, cron='{cron_schedule}', tz={user_timezone}") LOGGER.debug(f"[Stream-Mapparr] Schedule settings: enabled={enabled}, cron='{cron_schedule}', tz={user_timezone}")
# Sync the schedule # Sync the schedule
if enabled and cron_schedule: if enabled and cron_schedule:
@@ -1639,7 +1640,7 @@ class Plugin:
try: try:
# 1. Validate API connection and obtain token # 1. Validate API connection and obtain token
logger.info("[Stream-Mapparr] Validating API connection...") logger.debug("[Stream-Mapparr] Validating API connection...")
token, error = self.get_or_refresh_api_token(settings, logger) token, error = self.get_or_refresh_api_token(settings, logger)
if error: if error:
validation_results.append(f"❌ API Connection: {error}") validation_results.append(f"❌ API Connection: {error}")
@@ -1649,7 +1650,7 @@ class Plugin:
validation_results.append("✅ API Connection") validation_results.append("✅ API Connection")
# 2. Validate profile name exists # 2. Validate profile name exists
logger.info("[Stream-Mapparr] Validating profile names...") logger.debug("[Stream-Mapparr] Validating profile names...")
profile_names_str = settings.get("profile_name") or "" profile_names_str = settings.get("profile_name") or ""
profile_names_str = profile_names_str.strip() if profile_names_str else "" profile_names_str = profile_names_str.strip() if profile_names_str else ""
if not profile_names_str: if not profile_names_str:
@@ -1679,7 +1680,7 @@ class Plugin:
validation_results.append(f"✅ Profile Name ({len(found_profiles)})") validation_results.append(f"✅ Profile Name ({len(found_profiles)})")
# 3. Validate channel groups (if specified) # 3. Validate channel groups (if specified)
logger.info("[Stream-Mapparr] Validating channel groups...") logger.debug("[Stream-Mapparr] Validating channel groups...")
selected_groups_str = settings.get("selected_groups") or "" selected_groups_str = settings.get("selected_groups") or ""
selected_groups_str = selected_groups_str.strip() if selected_groups_str else "" selected_groups_str = selected_groups_str.strip() if selected_groups_str else ""
@@ -1696,7 +1697,7 @@ class Plugin:
except Exception as e: except Exception as e:
# If we get an error (e.g., 404 for non-existent page), we've reached the end # If we get an error (e.g., 404 for non-existent page), we've reached the end
if page > 1: if page > 1:
logger.info(f"[Stream-Mapparr] No more group pages available (attempted page {page})") logger.debug(f"[Stream-Mapparr] No more group pages available (attempted page {page})")
break break
else: else:
# If error on first page, re-raise # If error on first page, re-raise
@@ -1705,7 +1706,7 @@ class Plugin:
if isinstance(api_groups, dict) and 'results' in api_groups: if isinstance(api_groups, dict) and 'results' in api_groups:
results = api_groups['results'] results = api_groups['results']
if not results: if not results:
logger.info("[Stream-Mapparr] Reached last page of groups (empty results)") logger.debug("[Stream-Mapparr] Reached last page of groups (empty results)")
break break
all_groups.extend(results) all_groups.extend(results)
if not api_groups.get('next'): if not api_groups.get('next'):
@@ -1713,7 +1714,7 @@ class Plugin:
page += 1 page += 1
elif isinstance(api_groups, list): elif isinstance(api_groups, list):
if not api_groups: if not api_groups:
logger.info("[Stream-Mapparr] Reached last page of groups (empty results)") logger.debug("[Stream-Mapparr] Reached last page of groups (empty results)")
break break
all_groups.extend(api_groups) all_groups.extend(api_groups)
break break
@@ -1740,7 +1741,7 @@ class Plugin:
validation_results.append("✅ Channel Groups (all)") validation_results.append("✅ Channel Groups (all)")
# 4. Validate timezone is not empty # 4. Validate timezone is not empty
logger.info("[Stream-Mapparr] Validating timezone...") logger.debug("[Stream-Mapparr] Validating timezone...")
timezone_str = settings.get("timezone") or "US/Central" timezone_str = settings.get("timezone") or "US/Central"
timezone_str = timezone_str.strip() if timezone_str else "US/Central" timezone_str = timezone_str.strip() if timezone_str else "US/Central"
if not timezone_str: if not timezone_str:
@@ -1757,7 +1758,7 @@ class Plugin:
has_errors = True has_errors = True
# 5. Validate at least one channel database is checked # 5. Validate at least one channel database is checked
logger.info("[Stream-Mapparr] Validating channel databases...") logger.debug("[Stream-Mapparr] Validating channel databases...")
databases = self._get_channel_databases() databases = self._get_channel_databases()
if not databases: if not databases:
@@ -1817,7 +1818,7 @@ class Plugin:
cron_schedule = settings.get("schedule_cron") or "" cron_schedule = settings.get("schedule_cron") or ""
cron_schedule = cron_schedule.strip() if cron_schedule else "" cron_schedule = cron_schedule.strip() if cron_schedule else ""
logger.info(f"[Stream-Mapparr] Syncing schedule: enabled={enabled}, schedule='{cron_schedule}', tz={user_timezone}") logger.debug(f"[Stream-Mapparr] Syncing schedule: enabled={enabled}, schedule='{cron_schedule}', tz={user_timezone}")
if enabled and cron_schedule: if enabled and cron_schedule:
if not self._validate_cron(cron_schedule): if not self._validate_cron(cron_schedule):
@@ -1851,7 +1852,7 @@ class Plugin:
"""View active schedule""" """View active schedule"""
try: try:
user_timezone = settings.get("timezone", "America/Chicago") user_timezone = settings.get("timezone", "America/Chicago")
logger.info(f"[Stream-Mapparr] Viewing schedules with timezone: {user_timezone}") logger.debug(f"[Stream-Mapparr] Viewing schedules with timezone: {user_timezone}")
task_name = "stream_mapparr_scheduled_task" task_name = "stream_mapparr_scheduled_task"
task = PeriodicTask.objects.filter(name=task_name, enabled=True).first() task = PeriodicTask.objects.filter(name=task_name, enabled=True).first()
@@ -1961,7 +1962,7 @@ class Plugin:
limiter = SmartRateLimiter(settings.get("rate_limiting", "medium"), logger) limiter = SmartRateLimiter(settings.get("rate_limiting", "medium"), logger)
self._send_progress_update("load_process_channels", 'running', 5, 'Validating settings...', context) self._send_progress_update("load_process_channels", 'running', 5, 'Validating settings...', context)
logger.info("[Stream-Mapparr] Validating settings before loading channels...") logger.debug("[Stream-Mapparr] Validating settings before loading channels...")
has_errors, validation_results, token = self._validate_plugin_settings(settings, logger) has_errors, validation_results, token = self._validate_plugin_settings(settings, logger)
if has_errors: if has_errors:
@@ -2022,7 +2023,7 @@ class Plugin:
except Exception as e: except Exception as e:
# If we get an error (e.g., 404 for non-existent page), we've reached the end # If we get an error (e.g., 404 for non-existent page), we've reached the end
if page > 1: if page > 1:
logger.info(f"[Stream-Mapparr] No more group pages available (attempted page {page})") logger.debug(f"[Stream-Mapparr] No more group pages available (attempted page {page})")
break break
else: else:
# If error on first page, re-raise # If error on first page, re-raise
@@ -2031,7 +2032,7 @@ class Plugin:
if isinstance(api_groups, dict) and 'results' in api_groups: if isinstance(api_groups, dict) and 'results' in api_groups:
results = api_groups['results'] results = api_groups['results']
if not results: if not results:
logger.info("[Stream-Mapparr] Reached last page of groups (empty results)") logger.debug("[Stream-Mapparr] Reached last page of groups (empty results)")
break break
all_groups.extend(results) all_groups.extend(results)
if not api_groups.get('next'): if not api_groups.get('next'):
@@ -2039,7 +2040,7 @@ class Plugin:
page += 1 page += 1
elif isinstance(api_groups, list): elif isinstance(api_groups, list):
if not api_groups: if not api_groups:
logger.info("[Stream-Mapparr] Reached last page of groups (empty results)") logger.debug("[Stream-Mapparr] Reached last page of groups (empty results)")
break break
all_groups.extend(api_groups) all_groups.extend(api_groups)
break break
@@ -2094,7 +2095,7 @@ class Plugin:
except Exception as e: except Exception as e:
# If we get an error (e.g., 404 for non-existent page), we've reached the end # If we get an error (e.g., 404 for non-existent page), we've reached the end
if page > 1: if page > 1:
logger.info(f"[Stream-Mapparr] No more pages available (attempted page {page})") logger.debug(f"[Stream-Mapparr] No more pages available (attempted page {page})")
break break
else: else:
# If error on first page, re-raise # If error on first page, re-raise
@@ -2106,7 +2107,7 @@ class Plugin:
# Check if we got empty results # Check if we got empty results
if not results: if not results:
logger.info("[Stream-Mapparr] Reached last page of streams (empty results)") logger.debug("[Stream-Mapparr] Reached last page of streams (empty results)")
break break
all_streams_data.extend(results) all_streams_data.extend(results)
@@ -2114,14 +2115,14 @@ class Plugin:
# Stop if this page had fewer results than page_size (last page) # Stop if this page had fewer results than page_size (last page)
if len(results) < 100: if len(results) < 100:
logger.info("[Stream-Mapparr] Reached last page of streams") logger.debug("[Stream-Mapparr] Reached last page of streams")
break break
page += 1 page += 1
elif isinstance(streams_response, list): elif isinstance(streams_response, list):
# Check if we got empty results # Check if we got empty results
if not streams_response: if not streams_response:
logger.info("[Stream-Mapparr] Reached last page of streams (empty results)") logger.debug("[Stream-Mapparr] Reached last page of streams (empty results)")
break break
# List response - could still be paginated # List response - could still be paginated
@@ -2132,7 +2133,7 @@ class Plugin:
if len(streams_response) == 100: if len(streams_response) == 100:
page += 1 page += 1
else: else:
logger.info("[Stream-Mapparr] Reached last page of streams") logger.debug("[Stream-Mapparr] Reached last page of streams")
break break
else: else:
logger.warning("[Stream-Mapparr] Unexpected streams response format") logger.warning("[Stream-Mapparr] Unexpected streams response format")
@@ -2177,7 +2178,7 @@ class Plugin:
def _generate_csv_header_comment(self, settings, processed_data, total_visible_channels=0, total_matched_streams=0, low_match_channels=None, threshold_data=None): def _generate_csv_header_comment(self, settings, processed_data, total_visible_channels=0, total_matched_streams=0, low_match_channels=None, threshold_data=None):
"""Generate CSV comment header with plugin version and settings info.""" """Generate CSV comment header with plugin version and settings info."""
# Debug: Log all settings keys to see what's available # Debug: Log all settings keys to see what's available
LOGGER.info(f"[Stream-Mapparr] CSV generation - All settings keys: {list(settings.keys())}") LOGGER.debug(f"[Stream-Mapparr] CSV generation - All settings keys: {list(settings.keys())}")
profile_name = processed_data.get('profile_name', 'N/A') profile_name = processed_data.get('profile_name', 'N/A')
selected_groups = processed_data.get('selected_groups', []) selected_groups = processed_data.get('selected_groups', [])
@@ -2421,9 +2422,8 @@ class Plugin:
def preview_changes_action(self, settings, logger, context=None): def preview_changes_action(self, settings, logger, context=None):
"""Preview which streams will be added to channels without making changes.""" """Preview which streams will be added to channels without making changes."""
# Auto-load channels if not already loaded # Always reload channels to ensure fresh data
if not os.path.exists(self.processed_data_file): logger.info("[Stream-Mapparr] Loading fresh channel and stream data...")
logger.info("[Stream-Mapparr] No processed data found, loading channels automatically...")
self._send_progress_update("preview_changes", 'running', 0, 'Loading channels and streams...', context) self._send_progress_update("preview_changes", 'running', 0, 'Loading channels and streams...', context)
load_result = self.load_process_channels_action(settings, logger, context) load_result = self.load_process_channels_action(settings, logger, context)
if load_result.get('status') != 'success': if load_result.get('status') != 'success':
@@ -2477,6 +2477,7 @@ class Plugin:
processed_groups = 0 processed_groups = 0
total_groups = len(channel_groups) total_groups = len(channel_groups)
group_stats = {} # Track stats for each group
for group_key, group_channels in channel_groups.items(): for group_key, group_channels in channel_groups.items():
limiter.wait() limiter.wait()
@@ -2495,6 +2496,12 @@ class Plugin:
sorted_channels[0], streams, logger, ignore_tags, ignore_quality, ignore_regional, ignore_geographic, ignore_misc, channels_data sorted_channels[0], streams, logger, ignore_tags, ignore_quality, ignore_regional, ignore_geographic, ignore_misc, channels_data
) )
# Track group stats
group_stats[group_key] = {
'channel_count': len(group_channels),
'stream_count': len(matched_streams)
}
channels_to_update = sorted_channels[:visible_channel_limit] channels_to_update = sorted_channels[:visible_channel_limit]
channels_not_updated = sorted_channels[visible_channel_limit:] channels_not_updated = sorted_channels[visible_channel_limit:]
@@ -2562,6 +2569,13 @@ class Plugin:
"is_current": True "is_current": True
}) })
# Log channel group statistics
logger.info(f"[Stream-Mapparr] Processed {len(channel_groups)} channel groups with {len(channels)} total channels")
for group_key, stats in list(group_stats.items())[:10]: # Log first 10 groups
logger.info(f"[Stream-Mapparr] - Group '{group_key}': {stats['channel_count']} channel(s), {stats['stream_count']} matched stream(s)")
if len(channel_groups) > 10:
logger.info(f"[Stream-Mapparr] ... and {len(channel_groups) - 10} more groups")
self._send_progress_update("preview_changes", 'running', 85, 'Generating CSV report...', context) self._send_progress_update("preview_changes", 'running', 85, 'Generating CSV report...', context)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"stream_mapparr_preview_{timestamp}.csv" filename = f"stream_mapparr_preview_{timestamp}.csv"
@@ -2596,9 +2610,8 @@ class Plugin:
def add_streams_to_channels_action(self, settings, logger, is_scheduled=False, context=None): def add_streams_to_channels_action(self, settings, logger, is_scheduled=False, context=None):
"""Add matching streams to channels and replace existing stream assignments.""" """Add matching streams to channels and replace existing stream assignments."""
# Auto-load channels if not already loaded # Always reload channels to ensure fresh data
if not os.path.exists(self.processed_data_file): logger.info("[Stream-Mapparr] Loading fresh channel and stream data...")
logger.info("[Stream-Mapparr] No processed data found, loading channels automatically...")
self._send_progress_update("add_streams_to_channels", 'running', 0, 'Loading channels and streams...', context) self._send_progress_update("add_streams_to_channels", 'running', 0, 'Loading channels and streams...', context)
load_result = self.load_process_channels_action(settings, logger, context) load_result = self.load_process_channels_action(settings, logger, context)
if load_result.get('status') != 'success': if load_result.get('status') != 'success':
@@ -2649,6 +2662,7 @@ class Plugin:
processed_groups = 0 processed_groups = 0
total_groups = len(channel_groups) total_groups = len(channel_groups)
group_stats = {} # Track stats for each group
for group_key, group_channels in channel_groups.items(): for group_key, group_channels in channel_groups.items():
limiter.wait() # Rate limit processing limiter.wait() # Rate limit processing
@@ -2657,6 +2671,12 @@ class Plugin:
sorted_channels[0], streams, logger, ignore_tags, ignore_quality, ignore_regional, ignore_geographic, ignore_misc, channels_data sorted_channels[0], streams, logger, ignore_tags, ignore_quality, ignore_regional, ignore_geographic, ignore_misc, channels_data
) )
# Track group stats
group_stats[group_key] = {
'channel_count': len(group_channels),
'stream_count': len(matched_streams)
}
channels_to_update = sorted_channels[:visible_channel_limit] channels_to_update = sorted_channels[:visible_channel_limit]
for channel in channels_to_update: for channel in channels_to_update:
@@ -2695,6 +2715,13 @@ class Plugin:
self._send_progress_update("add_streams_to_channels", 'running', progress, self._send_progress_update("add_streams_to_channels", 'running', progress,
f'Updated {channels_updated} channels so far...', context) f'Updated {channels_updated} channels so far...', context)
# Log channel group statistics
logger.info(f"[Stream-Mapparr] Processed {len(channel_groups)} channel groups with {len(channels)} total channels")
for group_key, stats in list(group_stats.items())[:10]: # Log first 10 groups
logger.info(f"[Stream-Mapparr] - Group '{group_key}': {stats['channel_count']} channel(s), {stats['stream_count']} matched stream(s)")
if len(channel_groups) > 10:
logger.info(f"[Stream-Mapparr] ... and {len(channel_groups) - 10} more groups")
# CSV Export - create if setting is enabled # CSV Export - create if setting is enabled
# Default to True if setting doesn't exist (matches field default) # Default to True if setting doesn't exist (matches field default)
create_csv = settings.get('enable_scheduled_csv_export', True) create_csv = settings.get('enable_scheduled_csv_export', True)