From 6a85475402f763cc30c9ae8b1712e5e5ca2e30ec Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Sun, 19 Oct 2025 10:06:48 -0500 Subject: [PATCH] Enhancement: Adds ability to specify categories (comma separted) as well as include date and live tags in custom epg dummy output. --- apps/output/views.py | 129 +++++++++++++++++++++ frontend/src/components/forms/DummyEPG.jsx | 30 +++++ 2 files changed, 159 insertions(+) diff --git a/apps/output/views.py b/apps/output/views.py index 7ee4fe0c..6e93d07c 100644 --- a/apps/output/views.py +++ b/apps/output/views.py @@ -348,6 +348,13 @@ def generate_custom_dummy_programs(channel_id, channel_name, now, num_days, cust ended_title_template = custom_properties.get('ended_title_template', '') ended_description_template = custom_properties.get('ended_description_template', '') + # EPG metadata options + category_string = custom_properties.get('category', '') + # Split comma-separated categories and strip whitespace, filter out empty strings + categories = [cat.strip() for cat in category_string.split(',') if cat.strip()] if category_string else [] + include_date = custom_properties.get('include_date', True) + include_live = custom_properties.get('include_live', False) + # Parse timezone name try: source_tz = pytz.timezone(timezone_value) @@ -656,23 +663,53 @@ def generate_custom_dummy_programs(channel_id, channel_name, now, num_days, cust else: upcoming_description = f"Upcoming: {main_event_description}" + # Build custom_properties for upcoming programs (only date, no category/live) + program_custom_properties = {} + + # Add date if requested (YYYY-MM-DD format from start time in event timezone) + if include_date: + # Convert UTC time to event timezone for date calculation + local_time = program_start_utc.astimezone(source_tz) + date_str = local_time.strftime('%Y-%m-%d') + program_custom_properties['date'] = date_str + programs.append({ "channel_id": channel_id, "start_time": program_start_utc, "end_time": program_end_utc, "title": upcoming_title, "description": upcoming_description, + "custom_properties": program_custom_properties, }) current_time += timedelta(minutes=program_duration) # Add the MAIN EVENT at the extracted time + # Build custom_properties for main event (includes category and live) + main_event_custom_properties = {} + + # Add categories if provided + if categories: + main_event_custom_properties['categories'] = categories + + # Add date if requested (YYYY-MM-DD format from start time in event timezone) + if include_date: + # Convert UTC time to event timezone for date calculation + local_time = event_start_utc.astimezone(source_tz) + date_str = local_time.strftime('%Y-%m-%d') + main_event_custom_properties['date'] = date_str + + # Add live flag if requested + if include_live: + main_event_custom_properties['live'] = True + programs.append({ "channel_id": channel_id, "start_time": event_start_utc, "end_time": event_end_utc, "title": main_event_title, "description": main_event_description, + "custom_properties": main_event_custom_properties, }) event_happened = True @@ -695,12 +732,23 @@ def generate_custom_dummy_programs(channel_id, channel_name, now, num_days, cust else: ended_description = f"Ended: {main_event_description}" + # Build custom_properties for ended programs (only date, no category/live) + program_custom_properties = {} + + # Add date if requested (YYYY-MM-DD format from start time in event timezone) + if include_date: + # Convert UTC time to event timezone for date calculation + local_time = program_start_utc.astimezone(source_tz) + date_str = local_time.strftime('%Y-%m-%d') + program_custom_properties['date'] = date_str + programs.append({ "channel_id": channel_id, "start_time": program_start_utc, "end_time": program_end_utc, "title": ended_title, "description": ended_description, + "custom_properties": program_custom_properties, }) current_time += timedelta(minutes=program_duration) @@ -739,12 +787,23 @@ def generate_custom_dummy_programs(channel_id, channel_name, now, num_days, cust else: program_description = f"Upcoming: {main_event_description}" + # Build custom_properties (only date for upcoming/ended filler programs) + program_custom_properties = {} + + # Add date if requested (YYYY-MM-DD format from start time in event timezone) + if include_date: + # Convert UTC time to event timezone for date calculation + local_time = program_start_utc.astimezone(source_tz) + date_str = local_time.strftime('%Y-%m-%d') + program_custom_properties['date'] = date_str + programs.append({ "channel_id": channel_id, "start_time": program_start_utc, "end_time": program_end_utc, "title": program_title, "description": program_description, + "custom_properties": program_custom_properties, }) current_time += timedelta(minutes=program_duration) @@ -774,12 +833,31 @@ def generate_custom_dummy_programs(channel_id, channel_name, now, num_days, cust else: description = title + # Build custom_properties for this program + program_custom_properties = {} + + # Add categories if provided + if categories: + program_custom_properties['categories'] = categories + + # Add date if requested (YYYY-MM-DD format from start time in event timezone) + if include_date: + # Convert UTC time to event timezone for date calculation + local_time = program_start_utc.astimezone(source_tz) + date_str = local_time.strftime('%Y-%m-%d') + program_custom_properties['date'] = date_str + + # Add live flag if requested + if include_live: + program_custom_properties['live'] = True + programs.append({ "channel_id": channel_id, "start_time": program_start_utc, "end_time": program_end_utc, "title": title, "description": description, + "custom_properties": program_custom_properties, }) logger.info(f"Generated {len(programs)} custom dummy programs for {channel_name}") @@ -817,6 +895,23 @@ def generate_dummy_epg( ) xml_lines.append(f" {html.escape(program['title'])}") xml_lines.append(f" {html.escape(program['description'])}") + + # Add custom_properties if present + custom_data = program.get('custom_properties', {}) + + # Categories + if 'categories' in custom_data: + for cat in custom_data['categories']: + xml_lines.append(f" {html.escape(cat)}") + + # Date tag + if 'date' in custom_data: + xml_lines.append(f" {html.escape(custom_data['date'])}") + + # Live tag + if custom_data.get('live', False): + xml_lines.append(f" ") + xml_lines.append(f" ") return xml_lines @@ -999,6 +1094,23 @@ def generate_epg(request, profile_name=None, user=None): yield f' \n' yield f" {html.escape(program['title'])}\n" yield f" {html.escape(program['description'])}\n" + + # Add custom_properties if present + custom_data = program.get('custom_properties', {}) + + # Categories + if 'categories' in custom_data: + for cat in custom_data['categories']: + yield f" {html.escape(cat)}\n" + + # Date tag + if 'date' in custom_data: + yield f" {html.escape(custom_data['date'])}\n" + + # Live tag + if custom_data.get('live', False): + yield f" \n" + yield f" \n" else: @@ -1023,6 +1135,23 @@ def generate_epg(request, profile_name=None, user=None): yield f' \n' yield f" {html.escape(program['title'])}\n" yield f" {html.escape(program['description'])}\n" + + # Add custom_properties if present + custom_data = program.get('custom_properties', {}) + + # Categories + if 'categories' in custom_data: + for cat in custom_data['categories']: + yield f" {html.escape(cat)}\n" + + # Date tag + if 'date' in custom_data: + yield f" {html.escape(custom_data['date'])}\n" + + # Live tag + if custom_data.get('live', False): + yield f" \n" + yield f" \n" continue # Skip to next channel diff --git a/frontend/src/components/forms/DummyEPG.jsx b/frontend/src/components/forms/DummyEPG.jsx index 152d0164..a9608542 100644 --- a/frontend/src/components/forms/DummyEPG.jsx +++ b/frontend/src/components/forms/DummyEPG.jsx @@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { Box, Button, + Checkbox, Divider, Group, Modal, @@ -60,6 +61,9 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => { ended_description_template: '', name_source: 'channel', stream_index: 1, + category: '', + include_date: true, + include_live: false, }, }, validate: { @@ -345,6 +349,9 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => { ended_description_template: custom.ended_description_template || '', name_source: custom.name_source || 'channel', stream_index: custom.stream_index || 1, + category: custom.category || '', + include_date: custom.include_date ?? true, + include_live: custom.include_live ?? false, }, }); @@ -689,6 +696,29 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => { {...form.getInputProps('custom_properties.program_duration')} /> + + + + + + {/* Testing & Preview */}