mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-19 09:34:38 +00:00
- Add ProgramSearchResultSerializer with nested channel/stream data - Implement ProgramSearchAPIView with comprehensive filtering: * Text search with AND/OR operators and parenthetical grouping * Regex pattern matching support * Whole word matching to avoid partial matches * Time-based filtering (airing_at, start/end ranges) * Channel/stream/group filtering * Configurable field selection for response customization * Pagination (50 default, 500 max per page) - Add /api/epg/programs/search/ endpoint - Add comprehensive API documentation - Enhanced Swagger/OpenAPI schema with examples Examples: - Complex queries: (Newcastle OR NEW) AND (Villa OR AST) - Whole words: title=NEW&title_whole_words=true - Regex: title=^Premier&title_regex=true - Time filtering: airing_at=2026-02-14T20:00:00Z
112 lines
3.7 KiB
Python
112 lines
3.7 KiB
Python
from core.utils import validate_flexible_url
|
|
from rest_framework import serializers
|
|
from .models import EPGSource, EPGData, ProgramData
|
|
from apps.channels.models import Channel, Stream
|
|
|
|
class EPGSourceSerializer(serializers.ModelSerializer):
|
|
epg_data_count = serializers.SerializerMethodField()
|
|
read_only_fields = ['created_at', 'updated_at']
|
|
url = serializers.CharField(
|
|
required=False,
|
|
allow_blank=True,
|
|
allow_null=True,
|
|
validators=[validate_flexible_url]
|
|
)
|
|
|
|
class Meta:
|
|
model = EPGSource
|
|
fields = [
|
|
'id',
|
|
'name',
|
|
'source_type',
|
|
'url',
|
|
'api_key',
|
|
'is_active',
|
|
'file_path',
|
|
'refresh_interval',
|
|
'priority',
|
|
'status',
|
|
'last_message',
|
|
'created_at',
|
|
'updated_at',
|
|
'custom_properties',
|
|
'epg_data_count'
|
|
]
|
|
|
|
def get_epg_data_count(self, obj):
|
|
"""Return the count of EPG data entries instead of all IDs to prevent large payloads"""
|
|
return obj.epgs.count()
|
|
|
|
class ProgramDataSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = ProgramData
|
|
fields = ['id', 'start_time', 'end_time', 'title', 'sub_title', 'description', 'tvg_id']
|
|
|
|
class EPGDataSerializer(serializers.ModelSerializer):
|
|
"""
|
|
Only returns the tvg_id and the 'name' field from EPGData.
|
|
We assume 'name' is effectively the channel name.
|
|
"""
|
|
read_only_fields = ['epg_source']
|
|
|
|
class Meta:
|
|
model = EPGData
|
|
fields = [
|
|
'id',
|
|
'tvg_id',
|
|
'name',
|
|
'icon_url',
|
|
'epg_source',
|
|
]
|
|
|
|
|
|
class ProgramSearchChannelSerializer(serializers.ModelSerializer):
|
|
"""Lightweight channel info for search results."""
|
|
channel_group = serializers.CharField(source='channel_group.name', default=None)
|
|
|
|
class Meta:
|
|
model = Channel
|
|
fields = ['id', 'name', 'channel_number', 'channel_group', 'tvg_id']
|
|
|
|
|
|
class ProgramSearchStreamSerializer(serializers.ModelSerializer):
|
|
"""Lightweight stream info for search results."""
|
|
channel_group = serializers.CharField(source='channel_group.name', default=None)
|
|
m3u_account = serializers.CharField(source='m3u_account.name', default=None)
|
|
|
|
class Meta:
|
|
model = Stream
|
|
fields = ['id', 'name', 'channel_group', 'tvg_id', 'm3u_account']
|
|
|
|
|
|
class ProgramSearchResultSerializer(serializers.ModelSerializer):
|
|
"""Full program data with associated channels and streams for search results."""
|
|
epg_source = serializers.CharField(source='epg.epg_source.name', default=None)
|
|
epg_name = serializers.CharField(source='epg.name', default=None)
|
|
epg_icon_url = serializers.URLField(source='epg.icon_url', default=None)
|
|
channels = serializers.SerializerMethodField()
|
|
streams = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = ProgramData
|
|
fields = [
|
|
'id', 'title', 'sub_title', 'description',
|
|
'start_time', 'end_time', 'tvg_id', 'custom_properties',
|
|
'epg_source', 'epg_name', 'epg_icon_url',
|
|
'channels', 'streams',
|
|
]
|
|
|
|
def get_channels(self, obj):
|
|
channels = obj.epg.channels.all() if obj.epg else []
|
|
return ProgramSearchChannelSerializer(channels, many=True).data
|
|
|
|
def get_streams(self, obj):
|
|
channels = obj.epg.channels.all() if obj.epg else []
|
|
stream_ids = set()
|
|
streams = []
|
|
for ch in channels:
|
|
for s in ch.streams.all():
|
|
if s.id not in stream_ids:
|
|
stream_ids.add(s.id)
|
|
streams.append(s)
|
|
return ProgramSearchStreamSerializer(streams, many=True).data
|