Enhancement: Add custom fallback templates for dummy EPG generation to handle unmatched patterns

This commit is contained in:
SergeantPanda 2025-10-30 14:57:32 -05:00
parent 4284955412
commit 0741e45ce6
2 changed files with 131 additions and 2 deletions

View file

@ -187,6 +187,54 @@ def generate_m3u(request, profile_name=None, user=None):
return response
def generate_fallback_programs(channel_id, channel_name, now, num_days, program_length_hours, fallback_title, fallback_description):
"""
Generate dummy programs using custom fallback templates when patterns don't match.
Args:
channel_id: Channel ID for the programs
channel_name: Channel name to use as fallback in templates
now: Current datetime (in UTC)
num_days: Number of days to generate programs for
program_length_hours: Length of each program in hours
fallback_title: Custom fallback title template (empty string if not provided)
fallback_description: Custom fallback description template (empty string if not provided)
Returns:
List of program dictionaries
"""
programs = []
# Use custom fallback title or channel name as default
title = fallback_title if fallback_title else channel_name
# Use custom fallback description or a simple default message
if fallback_description:
description = fallback_description
else:
description = f"EPG information is currently unavailable for {channel_name}"
# Create programs for each day
for day in range(num_days):
day_start = now + timedelta(days=day)
# Create programs with specified length throughout the day
for hour_offset in range(0, 24, program_length_hours):
# Calculate program start and end times
start_time = day_start + timedelta(hours=hour_offset)
end_time = start_time + timedelta(hours=program_length_hours)
programs.append({
"channel_id": channel_id,
"start_time": start_time,
"end_time": end_time,
"title": title,
"description": description,
})
return programs
def generate_dummy_programs(channel_id, channel_name, num_days=1, program_length_hours=4, epg_source=None):
"""
Generate dummy EPG programs for channels.
@ -216,11 +264,26 @@ def generate_dummy_programs(channel_id, channel_name, num_days=1, program_length
epg_source.custom_properties
)
# If custom generation succeeded, return those programs
# If it returned empty (pattern didn't match), fall through to default
# If it returned empty (pattern didn't match), check for custom fallback templates
if custom_programs:
return custom_programs
else:
logger.info(f"Custom pattern didn't match for '{channel_name}', using default dummy EPG")
logger.info(f"Custom pattern didn't match for '{channel_name}', checking for custom fallback templates")
# Check if custom fallback templates are provided
custom_props = epg_source.custom_properties
fallback_title = custom_props.get('fallback_title_template', '').strip()
fallback_description = custom_props.get('fallback_description_template', '').strip()
# If custom fallback templates exist, use them instead of default
if fallback_title or fallback_description:
logger.info(f"Using custom fallback templates for '{channel_name}'")
return generate_fallback_programs(
channel_id, channel_name, now, num_days,
program_length_hours, fallback_title, fallback_description
)
else:
logger.info(f"No custom fallback templates found, using default dummy EPG")
# Default humorous program descriptions based on time of day
time_descriptions = {

View file

@ -49,6 +49,9 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
useState('');
const [endedTitleTemplate, setEndedTitleTemplate] = useState('');
const [endedDescriptionTemplate, setEndedDescriptionTemplate] = useState('');
const [fallbackTitleTemplate, setFallbackTitleTemplate] = useState('');
const [fallbackDescriptionTemplate, setFallbackDescriptionTemplate] =
useState('');
const [channelLogoUrl, setChannelLogoUrl] = useState('');
const [programPosterUrl, setProgramPosterUrl] = useState('');
const [timezoneOptions, setTimezoneOptions] = useState([]);
@ -73,6 +76,8 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
upcoming_description_template: '',
ended_title_template: '',
ended_description_template: '',
fallback_title_template: '',
fallback_description_template: '',
channel_logo_url: '',
program_poster_url: '',
name_source: 'channel',
@ -463,6 +468,9 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
custom.upcoming_description_template || '',
ended_title_template: custom.ended_title_template || '',
ended_description_template: custom.ended_description_template || '',
fallback_title_template: custom.fallback_title_template || '',
fallback_description_template:
custom.fallback_description_template || '',
channel_logo_url: custom.channel_logo_url || '',
program_poster_url: custom.program_poster_url || '',
name_source: custom.name_source || 'channel',
@ -487,6 +495,10 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
);
setEndedTitleTemplate(custom.ended_title_template || '');
setEndedDescriptionTemplate(custom.ended_description_template || '');
setFallbackTitleTemplate(custom.fallback_title_template || '');
setFallbackDescriptionTemplate(
custom.fallback_description_template || ''
);
setChannelLogoUrl(custom.channel_logo_url || '');
setProgramPosterUrl(custom.program_poster_url || '');
} else {
@ -501,6 +513,8 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
setUpcomingDescriptionTemplate('');
setEndedTitleTemplate('');
setEndedDescriptionTemplate('');
setFallbackTitleTemplate('');
setFallbackDescriptionTemplate('');
setChannelLogoUrl('');
setProgramPosterUrl('');
}
@ -571,6 +585,9 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
custom.upcoming_description_template || '',
ended_title_template: custom.ended_title_template || '',
ended_description_template: custom.ended_description_template || '',
fallback_title_template: custom.fallback_title_template || '',
fallback_description_template:
custom.fallback_description_template || '',
channel_logo_url: custom.channel_logo_url || '',
program_poster_url: custom.program_poster_url || '',
name_source: custom.name_source || 'channel',
@ -593,6 +610,8 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
setUpcomingDescriptionTemplate(custom.upcoming_description_template || '');
setEndedTitleTemplate(custom.ended_title_template || '');
setEndedDescriptionTemplate(custom.ended_description_template || '');
setFallbackTitleTemplate(custom.fallback_title_template || '');
setFallbackDescriptionTemplate(custom.fallback_description_template || '');
setChannelLogoUrl(custom.channel_logo_url || '');
setProgramPosterUrl(custom.program_poster_url || '');
@ -872,6 +891,53 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
}}
/>
{/* Fallback Templates */}
<Divider
label="Fallback Templates (Optional)"
labelPosition="center"
/>
<Text size="sm" c="dimmed">
When patterns don't match the channel/stream name, use these custom
fallback templates instead of the default placeholder messages.
Leave empty to use the built-in humorous fallback descriptions.
</Text>
<TextInput
id="fallback_title_template"
name="fallback_title_template"
label="Fallback Title Template"
description="Custom title when patterns don't match. If empty, uses the channel/stream name."
placeholder="No EPG data available"
value={fallbackTitleTemplate}
onChange={(e) => {
const value = e.target.value;
setFallbackTitleTemplate(value);
form.setFieldValue(
'custom_properties.fallback_title_template',
value
);
}}
/>
<Textarea
id="fallback_description_template"
name="fallback_description_template"
label="Fallback Description Template"
description="Custom description when patterns don't match. If empty, uses built-in placeholder messages."
placeholder="EPG information is currently unavailable for this channel."
minRows={2}
value={fallbackDescriptionTemplate}
onChange={(e) => {
const value = e.target.value;
setFallbackDescriptionTemplate(value);
form.setFieldValue(
'custom_properties.fallback_description_template',
value
);
}}
/>
{/* EPG Settings */}
<Divider label="EPG Settings" labelPosition="center" />