diff --git a/Stream-Mapparr/plugin.py b/Stream-Mapparr/plugin.py index 05583f1..a5b4e5c 100644 --- a/Stream-Mapparr/plugin.py +++ b/Stream-Mapparr/plugin.py @@ -158,6 +158,22 @@ class Plugin: "placeholder": "Sports, News, Entertainment", "help_text": "Specific channel groups to process, or leave empty for all groups.", }, + { + "id": "selected_stream_groups", + "label": "📺 Stream Groups (comma-separated)", + "type": "string", + "default": "", + "placeholder": "TVE, Cable, Satellite", + "help_text": "Specific stream groups to use when matching, or leave empty for all stream groups. Multiple groups can be specified separated by commas.", + }, + { + "id": "selected_m3us", + "label": "📡 M3U Sources (comma-separated)", + "type": "string", + "default": "", + "placeholder": "IPTV Provider 1, Local M3U, Sports", + "help_text": "Specific M3U sources to use when matching, or leave empty for all M3U sources. Multiple M3U sources can be specified separated by commas.", + }, { "id": "ignore_tags", "label": "🏷️ Ignore Tags (comma-separated)", @@ -1975,6 +1991,10 @@ class Plugin: profile_names_str = profile_names_str.strip() if profile_names_str else "" selected_groups_str = settings.get("selected_groups") or "" selected_groups_str = selected_groups_str.strip() if selected_groups_str else "" + selected_stream_groups_str = settings.get("selected_stream_groups") or "" + selected_stream_groups_str = selected_stream_groups_str.strip() if selected_stream_groups_str else "" + selected_m3us_str = settings.get("selected_m3us") or "" + selected_m3us_str = selected_m3us_str.strip() if selected_m3us_str else "" ignore_tags_str = settings.get("ignore_tags") or "" ignore_tags_str = ignore_tags_str.strip() if ignore_tags_str else "" visible_channel_limit_str = settings.get("visible_channel_limit", "1") @@ -2049,6 +2069,82 @@ class Plugin: group_name_to_id = {g['name']: g['id'] for g in all_groups if 'name' in g and 'id' in g} + # Fetch stream groups with rate limiting + self._send_progress_update("load_process_channels", 'running', 35, 'Fetching stream groups...', context) + all_stream_groups = [] + page = 1 + while True: + try: + api_stream_groups = self._get_api_data(f"/api/channels/stream-groups/?page={page}", token, settings, logger, limiter=limiter) + except Exception as e: + # If we get an error (e.g., 404 for non-existent page), we've reached the end + if page > 1: + logger.debug(f"[Stream-Mapparr] No more stream group pages available (attempted page {page})") + break + else: + # If error on first page, stream groups might not be available in this API version + logger.warning(f"[Stream-Mapparr] Could not fetch stream groups (API may not support this endpoint): {e}") + break + + if isinstance(api_stream_groups, dict) and 'results' in api_stream_groups: + results = api_stream_groups['results'] + if not results: + logger.debug("[Stream-Mapparr] Reached last page of stream groups (empty results)") + break + all_stream_groups.extend(results) + if not api_stream_groups.get('next'): + break + page += 1 + elif isinstance(api_stream_groups, list): + if not api_stream_groups: + logger.debug("[Stream-Mapparr] Reached last page of stream groups (empty results)") + break + all_stream_groups.extend(api_stream_groups) + break + else: + break + + stream_group_name_to_id = {g['name']: g['id'] for g in all_stream_groups if 'name' in g and 'id' in g} + logger.info(f"[Stream-Mapparr] Found {len(all_stream_groups)} stream groups") + + # Fetch M3U sources with rate limiting + self._send_progress_update("load_process_channels", 'running', 37, 'Fetching M3U sources...', context) + all_m3us = [] + page = 1 + while True: + try: + api_m3us = self._get_api_data(f"/api/channels/m3us/?page={page}", token, settings, logger, limiter=limiter) + except Exception as e: + # If we get an error (e.g., 404 for non-existent page), we've reached the end + if page > 1: + logger.debug(f"[Stream-Mapparr] No more M3U pages available (attempted page {page})") + break + else: + # If error on first page, M3Us might not be available in this API version + logger.warning(f"[Stream-Mapparr] Could not fetch M3U sources (API may not support this endpoint): {e}") + break + + if isinstance(api_m3us, dict) and 'results' in api_m3us: + results = api_m3us['results'] + if not results: + logger.debug("[Stream-Mapparr] Reached last page of M3Us (empty results)") + break + all_m3us.extend(results) + if not api_m3us.get('next'): + break + page += 1 + elif isinstance(api_m3us, list): + if not api_m3us: + logger.debug("[Stream-Mapparr] Reached last page of M3Us (empty results)") + break + all_m3us.extend(api_m3us) + break + else: + break + + m3u_name_to_id = {m['name']: m['id'] for m in all_m3us if 'name' in m and 'id' in m} + logger.info(f"[Stream-Mapparr] Found {len(all_m3us)} M3U sources") + # Fetch channels with rate limiting self._send_progress_update("load_process_channels", 'running', 40, 'Fetching channels...', context) all_channels = self._get_api_data("/api/channels/channels/", token, settings, logger, limiter=limiter) @@ -2139,6 +2235,42 @@ class Plugin: logger.warning("[Stream-Mapparr] Unexpected streams response format") break + # Filter streams by selected stream groups + if selected_stream_groups_str: + selected_stream_groups = [g.strip() for g in selected_stream_groups_str.split(',') if g.strip()] + valid_stream_group_ids = [stream_group_name_to_id[name] for name in selected_stream_groups if name in stream_group_name_to_id] + if not valid_stream_group_ids: + logger.warning("[Stream-Mapparr] None of the specified stream groups were found. Using all streams.") + selected_stream_groups = [] + stream_group_filter_info = " (all stream groups - specified groups not found)" + else: + # Filter streams by stream_group_id + filtered_streams = [s for s in all_streams_data if s.get('stream_group_id') in valid_stream_group_ids] + logger.info(f"[Stream-Mapparr] Filtered streams from {len(all_streams_data)} to {len(filtered_streams)} based on stream groups: {', '.join(selected_stream_groups)}") + all_streams_data = filtered_streams + stream_group_filter_info = f" in stream groups: {', '.join(selected_stream_groups)}" + else: + selected_stream_groups = [] + stream_group_filter_info = " (all stream groups)" + + # Filter streams by selected M3U sources + if selected_m3us_str: + selected_m3us = [m.strip() for m in selected_m3us_str.split(',') if m.strip()] + valid_m3u_ids = [m3u_name_to_id[name] for name in selected_m3us if name in m3u_name_to_id] + if not valid_m3u_ids: + logger.warning("[Stream-Mapparr] None of the specified M3U sources were found. Using all streams.") + selected_m3us = [] + m3u_filter_info = " (all M3U sources - specified M3Us not found)" + else: + # Filter streams by m3u_id + filtered_streams = [s for s in all_streams_data if s.get('m3u_id') in valid_m3u_ids] + logger.info(f"[Stream-Mapparr] Filtered streams from {len(all_streams_data)} to {len(filtered_streams)} based on M3U sources: {', '.join(selected_m3us)}") + all_streams_data = filtered_streams + m3u_filter_info = f" in M3U sources: {', '.join(selected_m3us)}" + else: + selected_m3us = [] + m3u_filter_info = " (all M3U sources)" + self.loaded_channels = channels_to_process self.loaded_streams = all_streams_data @@ -2149,6 +2281,8 @@ class Plugin: "profile_id": profile_id, "profile_ids": profile_ids, "selected_groups": selected_groups, + "selected_stream_groups": selected_stream_groups, + "selected_m3us": selected_m3us, "ignore_tags": ignore_tags, "visible_channel_limit": visible_channel_limit, "ignore_quality": ignore_quality, @@ -2182,8 +2316,10 @@ class Plugin: profile_name = processed_data.get('profile_name', 'N/A') selected_groups = processed_data.get('selected_groups', []) + selected_stream_groups = processed_data.get('selected_stream_groups', []) + selected_m3us = processed_data.get('selected_m3us', []) current_threshold = settings.get('fuzzy_match_threshold', 85) - + # Build header with all settings except login credentials header_lines = [ f"# Stream-Mapparr Export v{self.version}", @@ -2191,7 +2327,9 @@ class Plugin: "#", "# === Profile & Group Settings ===", f"# Profile Name(s): {profile_name}", - f"# Selected Groups: {', '.join(selected_groups) if selected_groups else '(all groups)'}", + f"# Selected Channel Groups: {', '.join(selected_groups) if selected_groups else '(all groups)'}", + f"# Selected Stream Groups: {', '.join(selected_stream_groups) if selected_stream_groups else '(all stream groups)'}", + f"# Selected M3U Sources: {', '.join(selected_m3us) if selected_m3us else '(all M3U sources)'}", "#", "# === Matching Settings ===", f"# Fuzzy Match Threshold: {current_threshold}",