From 990a9ec7b7cc69b46e4bcb001c591528ca23933d Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Thu, 3 Apr 2025 14:31:04 -0500 Subject: [PATCH] Added custom properties to programme --- apps/epg/tasks.py | 86 ++++++++++++++++++++++++++++++++++++++++++- apps/output/views.py | 88 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 171 insertions(+), 3 deletions(-) diff --git a/apps/epg/tasks.py b/apps/epg/tasks.py index 409e551e..fd75ec81 100644 --- a/apps/epg/tasks.py +++ b/apps/epg/tasks.py @@ -177,15 +177,97 @@ def parse_programs_for_tvg_id(epg_id): end_time = parse_xmltv_time(prog.get('stop')) title = prog.findtext('title', default='No Title') desc = prog.findtext('desc', default='') + sub_title = prog.findtext('sub-title', default='') + + # Extract custom properties + custom_props = {} + + # Extract categories + categories = [] + for cat_elem in prog.findall('category'): + if cat_elem.text and cat_elem.text.strip(): + categories.append(cat_elem.text.strip()) + if categories: + custom_props['categories'] = categories + + # Extract episode numbers + for ep_num in prog.findall('episode-num'): + system = ep_num.get('system', '') + if system == 'xmltv_ns' and ep_num.text: + # Parse XMLTV episode-num format (season.episode.part) + parts = ep_num.text.split('.') + if len(parts) >= 2: + if parts[0].strip() != '': + try: + season = int(parts[0]) + 1 # XMLTV format is zero-based + custom_props['season'] = season + except ValueError: + pass + if parts[1].strip() != '': + try: + episode = int(parts[1]) + 1 # XMLTV format is zero-based + custom_props['episode'] = episode + except ValueError: + pass + elif system == 'onscreen' and ep_num.text: + # Just store the raw onscreen format + custom_props['onscreen_episode'] = ep_num.text.strip() + + # Extract ratings + for rating_elem in prog.findall('rating'): + if rating_elem.findtext('value'): + custom_props['rating'] = rating_elem.findtext('value').strip() + if rating_elem.get('system'): + custom_props['rating_system'] = rating_elem.get('system') + break # Just use the first rating + + # Extract credits (actors, directors, etc.) + credits_elem = prog.find('credits') + if credits_elem is not None: + credits = {} + for credit_type in ['director', 'actor', 'writer', 'presenter', 'producer']: + elements = credits_elem.findall(credit_type) + if elements: + names = [e.text.strip() for e in elements if e.text and e.text.strip()] + if names: + credits[credit_type] = names + if credits: + custom_props['credits'] = credits + + # Extract other common program metadata + if prog.findtext('date'): + custom_props['year'] = prog.findtext('date').strip()[:4] # Just the year part + + if prog.findtext('country'): + custom_props['country'] = prog.findtext('country').strip() + + for icon_elem in prog.findall('icon'): + if icon_elem.get('src'): + custom_props['icon'] = icon_elem.get('src') + break # Just use the first icon + + for kw in ['previously-shown', 'premiere', 'new']: + if prog.find(kw) is not None: + custom_props[kw.replace('-', '_')] = True + + # Convert custom_props to JSON string if not empty + custom_properties_json = None + if custom_props: + import json + try: + custom_properties_json = json.dumps(custom_props) + except Exception as e: + logger.error(f"Error serializing custom properties to JSON: {e}", exc_info=True) programs_to_create.append(ProgramData( epg=epg, start_time=start_time, - title=title, end_time=end_time, + title=title, description=desc, - sub_title='', + sub_title=sub_title, tvg_id=epg.tvg_id, + custom_properties=custom_properties_json )) ProgramData.objects.bulk_create(programs_to_create) diff --git a/apps/output/views.py b/apps/output/views.py index b89df07a..369be3f5 100644 --- a/apps/output/views.py +++ b/apps/output/views.py @@ -105,7 +105,93 @@ def generate_epg(request, profile_name=None): stop_str = prog.end_time.strftime("%Y%m%d%H%M%S %z") xml_lines.append(f' ') xml_lines.append(f' {prog.title}') - xml_lines.append(f' {prog.description}') + + # Add subtitle if available + if prog.sub_title: + xml_lines.append(f' {prog.sub_title}') + + # Add description if available + if prog.description: + xml_lines.append(f' {prog.description}') + + # Process custom properties if available + if prog.custom_properties: + try: + import json + custom_data = json.loads(prog.custom_properties) + + # Add categories if available + if 'categories' in custom_data and custom_data['categories']: + for category in custom_data['categories']: + xml_lines.append(f' {category}') + + # Handle episode numbering - multiple formats supported + # Standard episode number if available + if 'episode' in custom_data: + xml_lines.append(f' E{custom_data["episode"]}') + + # Handle onscreen episode format (like S06E128) + if 'onscreen_episode' in custom_data: + xml_lines.append(f' {custom_data["onscreen_episode"]}') + + # Add season and episode numbers in xmltv_ns format if available + if 'season' in custom_data and 'episode' in custom_data: + season = int(custom_data['season']) - 1 if str(custom_data['season']).isdigit() else 0 + episode = int(custom_data['episode']) - 1 if str(custom_data['episode']).isdigit() else 0 + xml_lines.append(f' {season}.{episode}.') + + # Add rating if available + if 'rating' in custom_data: + rating_system = custom_data.get('rating_system', 'TV Parental Guidelines') + xml_lines.append(f' ') + xml_lines.append(f' {custom_data["rating"]}') + xml_lines.append(f' ') + + # Add actors/directors/writers if available + if 'credits' in custom_data: + xml_lines.append(f' ') + for role, people in custom_data['credits'].items(): + if isinstance(people, list): + for person in people: + xml_lines.append(f' <{role}>{person}') + else: + xml_lines.append(f' <{role}>{people}') + xml_lines.append(f' ') + + # Add program date/year if available + if 'year' in custom_data: + xml_lines.append(f' {custom_data["year"]}') + + # Add country if available + if 'country' in custom_data: + xml_lines.append(f' {custom_data["country"]}') + + # Add icon if available + if 'icon' in custom_data: + xml_lines.append(f' ') + + # Add special flags as proper tags + if custom_data.get('previously_shown', False): + xml_lines.append(f' ') + + if custom_data.get('premiere', False): + xml_lines.append(f' ') + + if custom_data.get('new', False): + xml_lines.append(f' ') + + # Define all properties we specifically handle to avoid adding them as comments + skip_keys = [ + 'categories', 'episode', 'season', 'rating', 'rating_system', 'credits', + 'year', 'country', 'icon', 'previously_shown', 'premiere', 'new', + 'onscreen_episode' + ] + + # Don't add comments for standard properties - XMLTV clients don't need them + # and they just clutter the output + except Exception as e: + xml_lines.append(f' ') + xml_lines.append(' ') xml_lines.append('')