Parse programs for all MAPPED channels from an EPG source in a single pass. This is an optimized version that: 1. Only processes EPG entries that are actually mapped to channels 2. Parses the XML file ONCE instead of once per channel 3. Skips programmes for unmapped channels en
(epg_source, tvg_id=None)
| 2060 | |
| 2061 | |
| 2062 | def parse_programs_for_source(epg_source, tvg_id=None): |
| 2063 | """ |
| 2064 | Parse programs for all MAPPED channels from an EPG source in a single pass. |
| 2065 | |
| 2066 | This is an optimized version that: |
| 2067 | 1. Only processes EPG entries that are actually mapped to channels |
| 2068 | 2. Parses the XML file ONCE instead of once per channel |
| 2069 | 3. Skips programmes for unmapped channels entirely during parsing |
| 2070 | |
| 2071 | This dramatically improves performance when an EPG source has many channels |
| 2072 | but only a fraction are mapped. |
| 2073 | """ |
| 2074 | # Send initial programs parsing notification |
| 2075 | send_epg_update(epg_source.id, "parsing_programs", 0) |
| 2076 | should_log_memory = False |
| 2077 | process = None |
| 2078 | initial_memory = 0 |
| 2079 | source_file = None |
| 2080 | |
| 2081 | # Add memory tracking only in trace mode or higher |
| 2082 | try: |
| 2083 | # Get current log level as a number |
| 2084 | current_log_level = logger.getEffectiveLevel() |
| 2085 | |
| 2086 | # Only track memory usage when log level is TRACE or more verbose |
| 2087 | should_log_memory = current_log_level <= 5 or settings.DEBUG # Assuming TRACE is level 5 or lower |
| 2088 | |
| 2089 | if should_log_memory: |
| 2090 | process = psutil.Process() |
| 2091 | initial_memory = process.memory_info().rss / 1024 / 1024 |
| 2092 | logger.info(f"[parse_programs_for_source] Initial memory usage: {initial_memory:.2f} MB") |
| 2093 | except ImportError: |
| 2094 | logger.warning("psutil not available for memory tracking") |
| 2095 | process = None |
| 2096 | should_log_memory = False |
| 2097 | |
| 2098 | try: |
| 2099 | # Only get EPG entries that are actually mapped to channels |
| 2100 | mapped_epg_ids = set( |
| 2101 | Channel.objects.filter( |
| 2102 | epg_data__epg_source=epg_source, |
| 2103 | epg_data__isnull=False |
| 2104 | ).values_list('epg_data_id', flat=True) |
| 2105 | ) |
| 2106 | |
| 2107 | if not mapped_epg_ids: |
| 2108 | total_epg_count = EPGData.objects.filter(epg_source=epg_source).count() |
| 2109 | logger.info(f"No channels mapped to any EPG entries from source: {epg_source.name} " |
| 2110 | f"(source has {total_epg_count} EPG entries, 0 mapped)") |
| 2111 | # Update status - this is not an error, just no mapped entries |
| 2112 | epg_source.status = 'success' |
| 2113 | epg_source.last_message = f"No channels mapped to this EPG source ({total_epg_count} entries available)" |
| 2114 | epg_source.save(update_fields=['status', 'last_message']) |
| 2115 | send_epg_update(epg_source.id, "parsing_programs", 100, status="success") |
| 2116 | return True |
| 2117 | |
| 2118 | # Get the mapped EPG entries with their tvg_ids |
| 2119 | mapped_epgs = EPGData.objects.filter(id__in=mapped_epg_ids).values('id', 'tvg_id') |