From 1c47b7f84ac121280551f576a666f29477547a36 Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Fri, 1 Aug 2025 16:42:42 -0500 Subject: [PATCH] Adds ability to reverse the sort order for auto channel sync. --- apps/m3u/tasks.py | 19 ++-- .../src/components/forms/M3UGroupFilter.jsx | 94 +++++++++++++------ 2 files changed, 77 insertions(+), 36 deletions(-) diff --git a/apps/m3u/tasks.py b/apps/m3u/tasks.py index 40a395ce..7598211a 100644 --- a/apps/m3u/tasks.py +++ b/apps/m3u/tasks.py @@ -884,6 +884,7 @@ def sync_auto_channels(account_id, scan_start_time=None): name_match_regex = None channel_profile_ids = None channel_sort_order = None + channel_sort_reverse = False if group_relation.custom_properties: try: group_custom_props = json.loads(group_relation.custom_properties) @@ -894,6 +895,7 @@ def sync_auto_channels(account_id, scan_start_time=None): name_match_regex = group_custom_props.get("name_match_regex") channel_profile_ids = group_custom_props.get("channel_profile_ids") channel_sort_order = group_custom_props.get("channel_sort_order") + channel_sort_reverse = group_custom_props.get("channel_sort_reverse", False) except Exception: force_dummy_epg = False override_group_id = None @@ -902,6 +904,7 @@ def sync_auto_channels(account_id, scan_start_time=None): name_match_regex = None channel_profile_ids = None channel_sort_order = None + channel_sort_reverse = False # Determine which group to use for created channels target_group = channel_group @@ -936,18 +939,22 @@ def sync_auto_channels(account_id, scan_start_time=None): if channel_sort_order == 'name': # Use natural sorting for names to handle numbers correctly current_streams = list(current_streams) - current_streams.sort(key=lambda stream: natural_sort_key(stream.name)) + current_streams.sort(key=lambda stream: natural_sort_key(stream.name), reverse=channel_sort_reverse) streams_is_list = True elif channel_sort_order == 'tvg_id': - current_streams = current_streams.order_by('tvg_id') + order_prefix = '-' if channel_sort_reverse else '' + current_streams = current_streams.order_by(f'{order_prefix}tvg_id') elif channel_sort_order == 'updated_at': - current_streams = current_streams.order_by('updated_at') + order_prefix = '-' if channel_sort_reverse else '' + current_streams = current_streams.order_by(f'{order_prefix}updated_at') else: logger.warning(f"Unknown channel_sort_order '{channel_sort_order}' for group '{channel_group.name}'. Using provider order.") - current_streams = current_streams.order_by('id') + order_prefix = '-' if channel_sort_reverse else '' + current_streams = current_streams.order_by(f'{order_prefix}id') else: - current_streams = current_streams.order_by('id') - # If channel_sort_order is empty or None, use provider order (no additional sorting) + # Provider order (default) - can still be reversed + order_prefix = '-' if channel_sort_reverse else '' + current_streams = current_streams.order_by(f'{order_prefix}id') # Get existing auto-created channels for this account (regardless of current group) # We'll find them by their stream associations instead of just group location diff --git a/frontend/src/components/forms/M3UGroupFilter.jsx b/frontend/src/components/forms/M3UGroupFilter.jsx index e5918375..64c1d356 100644 --- a/frontend/src/components/forms/M3UGroupFilter.jsx +++ b/frontend/src/components/forms/M3UGroupFilter.jsx @@ -410,8 +410,13 @@ const M3UGroupFilter = ({ playlist = null, isOpen, onClose }) => { if (newCustomProps.channel_sort_order === undefined) { newCustomProps.channel_sort_order = ''; } + // Keep channel_sort_reverse if it exists + if (newCustomProps.channel_sort_reverse === undefined) { + newCustomProps.channel_sort_reverse = false; + } } else { delete newCustomProps.channel_sort_order; + delete newCustomProps.channel_sort_reverse; // Remove reverse when sort is removed } return { @@ -428,36 +433,65 @@ const M3UGroupFilter = ({ playlist = null, isOpen, onClose }) => { /> {/* Show only channel_sort_order if selected */} {group.custom_properties?.channel_sort_order !== undefined && ( - { + setGroupStates( + groupStates.map((state) => { + if (state.channel_group === group.channel_group) { + return { + ...state, + custom_properties: { + ...state.custom_properties, + channel_sort_order: value || '', + }, + }; + } + return state; + }) + ); + }} + data={[ + { value: '', label: 'Provider Order (Default)' }, + { value: 'name', label: 'Name' }, + { value: 'tvg_id', label: 'TVG ID' }, + { value: 'updated_at', label: 'Updated At' }, + ]} + clearable + searchable + size="xs" + /> + + {/* Add reverse sort checkbox when sort order is selected (including default) */} + {group.custom_properties?.channel_sort_order !== undefined && ( + + { + setGroupStates( + groupStates.map((state) => { + if (state.channel_group === group.channel_group) { + return { + ...state, + custom_properties: { + ...state.custom_properties, + channel_sort_reverse: event.target.checked, + }, + }; + } + return state; + }) + ); + }} + size="xs" + /> + + )} + )} {/* Show profile selection only if profile_assignment is selected */}