diff --git a/apps/channels/migrations/0019_channel_tvc_guide_stationid.py b/apps/channels/migrations/0019_channel_tvc_guide_stationid.py new file mode 100644 index 00000000..86f76b82 --- /dev/null +++ b/apps/channels/migrations/0019_channel_tvc_guide_stationid.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.6 on 2025-05-04 00:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dispatcharr_channels', '0018_channelgroupm3uaccount_custom_properties_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='channel', + name='tvc_guide_stationid', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/apps/channels/models.py b/apps/channels/models.py index f158c2d0..dc5c50fb 100644 --- a/apps/channels/models.py +++ b/apps/channels/models.py @@ -236,6 +236,8 @@ class Channel(models.Model): help_text="Channel group this channel belongs to." ) tvg_id = models.CharField(max_length=255, blank=True, null=True) + tvc_guide_stationid = models.CharField(max_length=255, blank=True, null=True) + epg_data = models.ForeignKey( EPGData, on_delete=models.SET_NULL, diff --git a/apps/channels/serializers.py b/apps/channels/serializers.py index a23e4637..ce3910a4 100644 --- a/apps/channels/serializers.py +++ b/apps/channels/serializers.py @@ -152,6 +152,7 @@ class ChannelSerializer(serializers.ModelSerializer): 'name', 'channel_group_id', 'tvg_id', + 'tvc_guide_stationid', 'epg_data_id', 'streams', 'stream_profile_id', diff --git a/apps/output/views.py b/apps/output/views.py index 9429d9ca..5a5335d3 100644 --- a/apps/output/views.py +++ b/apps/output/views.py @@ -30,12 +30,17 @@ def generate_m3u(request, profile_name=None): tvg_logo = "" if channel.logo: tvg_logo = request.build_absolute_uri(reverse('api:channels:logo-cache', args=[channel.logo.id])) + + # create possible gracenote id insertion + tvc_guide_stationid = "" + if len(channel.tvc_guide_stationid) > 0: + tvc_guide_stationid = f'tvc-guide-stationid="{channel.tvc_guide_stationid}" ' channel_number = channel.channel_number extinf_line = ( f'#EXTINF:-1 tvg-id="{tvg_id}" tvg-name="{tvg_name}" tvg-logo="{tvg_logo}" ' - f'tvg-chno="{channel_number}" group-title="{group_title}",{channel.name}\n' + f'tvg-chno="{channel_number}" {tvc_guide_stationid}group-title="{group_title}",{channel.name}\n' ) base_url = request.build_absolute_uri('/')[:-1] diff --git a/frontend/src/api.js b/frontend/src/api.js index 274ff1fa..b2ecf820 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -347,6 +347,11 @@ export default class API { payload.tvg_id = null; } + // Ensure tvc_guide_stationid is included properly (not as empty string) + if (payload.tvc_guide_stationid === '') { + payload.tvc_guide_stationid = null; + } + // Handle channel_number properly if (payload.channel_number === '') { payload.channel_number = null; diff --git a/frontend/src/components/forms/Channel.jsx b/frontend/src/components/forms/Channel.jsx index 5c913060..5c7243e9 100644 --- a/frontend/src/components/forms/Channel.jsx +++ b/frontend/src/components/forms/Channel.jsx @@ -98,6 +98,7 @@ const Channel = ({ channel = null, isOpen, onClose }) => { channel_group_id: Object.keys(channelGroups)[0], stream_profile_id: '0', tvg_id: '', + tvc_guide_stationid: '', epg_data_id: '', logo_id: '', }, @@ -122,6 +123,9 @@ const Channel = ({ channel = null, isOpen, onClose }) => { // Ensure tvg_id is properly included (no empty strings) formattedValues.tvg_id = formattedValues.tvg_id || null; + // Ensure tvc_guide_stationid is properly included (no empty strings) + formattedValues.tvc_guide_stationid = formattedValues.tvc_guide_stationid || null; + if (channel) { // If there's an EPG to set, use our enhanced endpoint if (values.epg_data_id !== (channel.epg_data_id ?? '')) { @@ -187,6 +191,7 @@ const Channel = ({ channel = null, isOpen, onClose }) => { ? `${channel.stream_profile_id}` : '0', tvg_id: channel.tvg_id, + tvc_guide_stationid: channel.tvc_guide_stationid, epg_data_id: channel.epg_data_id ?? '', logo_id: `${channel.logo_id}`, }); @@ -673,6 +678,16 @@ const Channel = ({ channel = null, isOpen, onClose }) => { error={formik.errors.tvg_id ? formik.touched.tvg_id : ''} size="xs" /> + +