mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-18 00:55:50 +00:00
Batch ProgramData query and invalidate index on EPG refresh
- Replace per-entry ProgramData DB query with single batch fetch - Pass pre-loaded EPGData objects to find_current_program_for_tvg_id to avoid redundant select_related re-fetch - Delete Redis programme index before XMLTV download so stale byte offsets are never used during the refresh window
This commit is contained in:
parent
8f1dd2ffeb
commit
3c5032ee71
2 changed files with 33 additions and 14 deletions
|
|
@ -765,7 +765,7 @@ class CurrentProgramsAPIView(APIView):
|
|||
)
|
||||
|
||||
try:
|
||||
epg_data_ids = [int(id) for id in epg_data_ids]
|
||||
epg_data_ids = [int(eid) for eid in epg_data_ids]
|
||||
except (ValueError, TypeError):
|
||||
return Response(
|
||||
{"error": "epg_data_ids must contain valid integers"},
|
||||
|
|
@ -777,12 +777,20 @@ class CurrentProgramsAPIView(APIView):
|
|||
|
||||
epg_data_entries = EPGData.objects.select_related('epg_source').filter(id__in=epg_data_ids)
|
||||
|
||||
# Batch-fetch current programs for all requested EPG entries in one query
|
||||
db_programs = ProgramData.objects.filter(
|
||||
epg__in=epg_data_entries, start_time__lte=now, end_time__gt=now
|
||||
).select_related('epg')
|
||||
# Map epg_data id -> first matching program
|
||||
programs_by_epg = {}
|
||||
for prog in db_programs:
|
||||
if prog.epg_id not in programs_by_epg:
|
||||
programs_by_epg[prog.epg_id] = prog
|
||||
|
||||
current_programs = []
|
||||
for epg_data in epg_data_entries:
|
||||
# Check DB first (already-parsed channels)
|
||||
program = ProgramData.objects.filter(
|
||||
epg=epg_data, start_time__lte=now, end_time__gt=now
|
||||
).first()
|
||||
# Check batch-fetched DB results first
|
||||
program = programs_by_epg.get(epg_data.id)
|
||||
|
||||
if program:
|
||||
program_data = ProgramDataSerializer(program).data
|
||||
|
|
@ -794,8 +802,8 @@ class CurrentProgramsAPIView(APIView):
|
|||
if epg_data.epg_source and epg_data.epg_source.source_type == 'dummy':
|
||||
continue
|
||||
|
||||
# Fall back to byte-offset index lookup
|
||||
result = find_current_program_for_tvg_id(epg_data.id)
|
||||
# Fall back to byte-offset index lookup, pass the object to avoid re-fetch
|
||||
result = find_current_program_for_tvg_id(epg_data)
|
||||
|
||||
if result == "timeout":
|
||||
current_programs.append({
|
||||
|
|
|
|||
|
|
@ -296,6 +296,14 @@ def refresh_epg_data(source_id):
|
|||
# Continue with the normal processing...
|
||||
logger.info(f"Processing EPGSource: {source.name} (type: {source.source_type})")
|
||||
if source.source_type == 'xmltv':
|
||||
# Invalidate the byte-offset index before downloading the new file
|
||||
# so stale offsets are never used during the refresh window.
|
||||
from core.utils import RedisClient
|
||||
try:
|
||||
RedisClient.get_client().delete(f'epg_programme_index_{source.id}')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
fetch_success = fetch_xmltv(source)
|
||||
if not fetch_success:
|
||||
logger.error(f"Failed to fetch XMLTV for source {source.name}")
|
||||
|
|
@ -2499,19 +2507,22 @@ def build_programme_index_task(source_id):
|
|||
build_programme_index(source_id)
|
||||
|
||||
|
||||
def find_current_program_for_tvg_id(epg_id):
|
||||
def find_current_program_for_tvg_id(epg_or_id):
|
||||
"""
|
||||
Look up the currently-airing program for an EPGData id using the
|
||||
byte-offset index. Falls back to sequential scan if no index exists.
|
||||
Look up the currently-airing program for an EPGData instance (or id) using
|
||||
the byte-offset index. Falls back to sequential scan if no index exists.
|
||||
|
||||
Returns dict, None, or "timeout".
|
||||
"""
|
||||
from core.utils import RedisClient
|
||||
|
||||
try:
|
||||
epg = EPGData.objects.select_related('epg_source').get(id=epg_id)
|
||||
except EPGData.DoesNotExist:
|
||||
return None
|
||||
if isinstance(epg_or_id, EPGData):
|
||||
epg = epg_or_id
|
||||
else:
|
||||
try:
|
||||
epg = EPGData.objects.select_related('epg_source').get(id=epg_or_id)
|
||||
except EPGData.DoesNotExist:
|
||||
return None
|
||||
|
||||
source = epg.epg_source
|
||||
if not source or source.source_type == 'dummy':
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue