mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-18 00:55:50 +00:00
Fix: Auto-parse EPG programs for unlinked channels on preview
When selecting an EPG channel in the channel form, if that EPG entry's programs had never been parsed, the preview showed "No current program". This was because parse_programs_for_tvg_id requires a linked channel. - Add force parameter to parse_programs_for_tvg_id to bypass channel check - Auto-trigger parsing from current-programs API when no programs exist - Check Redis task lock to prevent duplicate parse tasks from concurrent requests - Frontend retries up to 3 times with 10s delay to pick up parsed results - Skip parsing for dummy EPG sources
This commit is contained in:
parent
e733b9872f
commit
ef1518a8dd
4 changed files with 46 additions and 16 deletions
|
|
@ -811,6 +811,20 @@ class CurrentProgramsAPIView(APIView):
|
|||
program_data = ProgramDataSerializer(program).data
|
||||
program_data["epg_data_id"] = epg_data.id
|
||||
current_programs.append(program_data)
|
||||
else:
|
||||
# Check if ANY programs exist (vs none parsed yet)
|
||||
has_any_programs = ProgramData.objects.filter(epg=epg_data).exists()
|
||||
if not has_any_programs and epg_data.epg_source.source_type != 'dummy':
|
||||
from core.utils import RedisClient
|
||||
redis_client = RedisClient.get_client()
|
||||
lock_id = f"task_lock_parse_epg_programs_{epg_data.id}"
|
||||
if not redis_client.exists(lock_id):
|
||||
from apps.epg.tasks import parse_programs_for_tvg_id
|
||||
parse_programs_for_tvg_id.delay(epg_data.id, force=True)
|
||||
current_programs.append({
|
||||
"epg_data_id": epg_data.id,
|
||||
"parsing": True,
|
||||
})
|
||||
|
||||
return Response(current_programs, status=status.HTTP_200_OK)
|
||||
|
||||
|
|
|
|||
|
|
@ -1254,7 +1254,7 @@ def parse_channels_only(source):
|
|||
|
||||
|
||||
@shared_task(time_limit=3600, soft_time_limit=3500)
|
||||
def parse_programs_for_tvg_id(epg_id):
|
||||
def parse_programs_for_tvg_id(epg_id, force=False):
|
||||
if not acquire_task_lock('parse_epg_programs', epg_id):
|
||||
logger.info(f"Program parse for {epg_id} already in progress, skipping duplicate task")
|
||||
return "Task already running"
|
||||
|
|
@ -1295,7 +1295,7 @@ def parse_programs_for_tvg_id(epg_id):
|
|||
release_task_lock('parse_epg_programs', epg_id)
|
||||
return
|
||||
|
||||
if not Channel.objects.filter(epg_data=epg).exists():
|
||||
if not force and not Channel.objects.filter(epg_data=epg).exists():
|
||||
logger.info(f"No channels matched to EPG {epg.tvg_id}")
|
||||
lock_renewer.stop()
|
||||
release_task_lock('parse_epg_programs', epg_id)
|
||||
|
|
|
|||
|
|
@ -1540,7 +1540,13 @@ export default class API {
|
|||
);
|
||||
|
||||
// The endpoint returns an array, get the first item
|
||||
return response && response.length > 0 ? response[0] : null;
|
||||
if (response && response.length > 0) {
|
||||
if (response[0].parsing) {
|
||||
return { parsing: true };
|
||||
}
|
||||
return response[0];
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
console.error('Failed to retrieve current program for EPG', e);
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -474,24 +474,34 @@ const ChannelForm = ({ channel: channelProp = null, isOpen, onClose }) => {
|
|||
setIsLoadingProgram(true);
|
||||
setHasFetchedProgram(false);
|
||||
|
||||
API.getCurrentProgramForEpg(epgDataId)
|
||||
.then((program) => {
|
||||
if (!cancelled) {
|
||||
setCurrentProgram(program);
|
||||
const fetchWithRetry = async (retriesLeft = 3) => {
|
||||
try {
|
||||
const program = await API.getCurrentProgramForEpg(epgDataId);
|
||||
if (cancelled) return;
|
||||
|
||||
if (program && program.parsing && retriesLeft > 0) {
|
||||
// Programs are being parsed, retry after delay
|
||||
await new Promise((resolve) => setTimeout(resolve, 10000));
|
||||
if (cancelled) return;
|
||||
return fetchWithRetry(retriesLeft - 1);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!cancelled) {
|
||||
console.error('Failed to fetch current program:', error);
|
||||
setCurrentProgram(null);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
if (!cancelled) {
|
||||
setCurrentProgram(program && !program.parsing ? program : null);
|
||||
setIsLoadingProgram(false);
|
||||
setHasFetchedProgram(true);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
if (!cancelled) {
|
||||
console.error('Failed to fetch current program:', error);
|
||||
setCurrentProgram(null);
|
||||
setIsLoadingProgram(false);
|
||||
setHasFetchedProgram(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchWithRetry();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue