channel streams optimizations, bug fixes in tables, parallel api calls for paginated data fetching

This commit is contained in:
dekzter 2025-04-24 17:14:19 -04:00
parent 1d63075660
commit 8fe83fd9ee
10 changed files with 129 additions and 64 deletions

View file

@ -11,6 +11,7 @@ from .api_views import (
UpdateChannelMembershipAPIView,
BulkUpdateChannelMembershipAPIView,
RecordingViewSet,
GetChannelStreamsAPIView,
)
app_name = 'channels' # for DRF routing
@ -27,6 +28,7 @@ urlpatterns = [
# Bulk delete is a single APIView, not a ViewSet
path('streams/bulk-delete/', BulkDeleteStreamsAPIView.as_view(), name='bulk_delete_streams'),
path('channels/bulk-delete/', BulkDeleteChannelsAPIView.as_view(), name='bulk_delete_channels'),
path('channels/<int:channel_id>/streams/', GetChannelStreamsAPIView.as_view(), name='get_channel_streams'),
path('profiles/<int:profile_id>/channels/<int:channel_id>/', UpdateChannelMembershipAPIView.as_view(), name='update_channel_membership'),
path('profiles/<int:profile_id>/channels/bulk-update/', BulkUpdateChannelMembershipAPIView.as_view(), name='bulk_update_channel_membership'),
]

View file

@ -6,7 +6,7 @@ from rest_framework.decorators import action
from rest_framework.parsers import MultiPartParser, FormParser
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from django.shortcuts import get_object_or_404
from django.shortcuts import get_object_or_404, get_list_or_404
from django.db import transaction
import os, json, requests
@ -88,6 +88,16 @@ class StreamViewSet(viewsets.ModelViewSet):
return qs
def list(self, request, *args, **kwargs):
ids = request.query_params.get('ids', None)
if ids:
ids = ids.split(',')
streams = get_list_or_404(Stream, id__in=ids)
serializer = self.get_serializer(streams, many=True)
return Response(serializer.data)
return super().list(request, *args, **kwargs)
@action(detail=False, methods=['get'], url_path='ids')
def get_ids(self, request, *args, **kwargs):
# Get the filtered queryset
@ -169,6 +179,12 @@ class ChannelViewSet(viewsets.ModelViewSet):
return qs
def get_serializer_context(self):
context = super().get_serializer_context()
include_streams = self.request.query_params.get('include_streams', 'false') == 'true'
context['include_streams'] = include_streams
return context
@action(detail=False, methods=['get'], url_path='ids')
def get_ids(self, request, *args, **kwargs):
# Get the filtered queryset
@ -629,6 +645,13 @@ class ChannelProfileViewSet(viewsets.ModelViewSet):
serializer_class = ChannelProfileSerializer
permission_classes = [IsAuthenticated]
class GetChannelStreamsAPIView(APIView):
def get(self, request, channel_id):
channel = get_object_or_404(Channel, id=channel_id)
streams = channel.streams
serializer = StreamSerializer(streams, many=True)
return Response(serializer.data)
class UpdateChannelMembershipAPIView(APIView):
def patch(self, request, profile_id, channel_id):
"""Enable or disable a channel for a specific group"""

View file

@ -134,10 +134,7 @@ class ChannelSerializer(serializers.ModelSerializer):
required=False
)
streams = serializers.SerializerMethodField()
stream_ids = serializers.PrimaryKeyRelatedField(
queryset=Stream.objects.all(), many=True, write_only=True, required=False
)
streams = serializers.PrimaryKeyRelatedField(queryset=Stream.objects.all(), many=True, required=False)
logo_id = serializers.PrimaryKeyRelatedField(
queryset=Logo.objects.all(),
@ -156,23 +153,25 @@ class ChannelSerializer(serializers.ModelSerializer):
'tvg_id',
'epg_data_id',
'streams',
'stream_ids',
'stream_profile_id',
'uuid',
'logo_id',
]
def get_streams(self, obj):
"""Retrieve ordered stream objects for GET requests."""
ordered_streams = obj.streams.all().order_by('channelstream__order')
return StreamSerializer(ordered_streams, many=True).data
def to_representation(self, instance):
include_streams = self.context.get('include_streams', False)
if include_streams:
self.fields['streams'] = serializers.SerializerMethodField()
return super().to_representation(instance)
def get_logo(self, obj):
return LogoSerializer(obj.logo).data
# def get_stream_ids(self, obj):
# """Retrieve ordered stream IDs for GET requests."""
# return list(obj.streams.all().order_by('channelstream__order').values_list('id', flat=True))
def get_streams(self, obj):
"""Retrieve ordered stream IDs for GET requests."""
return StreamSerializer(obj.streams.all().order_by('channelstream__order'), many=True).data
def create(self, validated_data):
stream_ids = validated_data.pop('streams', [])
@ -185,8 +184,9 @@ class ChannelSerializer(serializers.ModelSerializer):
ChannelStream.objects.create(channel=channel, stream_id=stream_id, order=index)
return channel
def update(self, instance, validated_data):
stream_ids = validated_data.pop('stream_ids', None)
streams = validated_data.pop('streams', None)
# Update standard fields
for attr, value in validated_data.items():
@ -194,12 +194,13 @@ class ChannelSerializer(serializers.ModelSerializer):
instance.save()
if stream_ids is not None:
if streams is not None:
# Normalize stream IDs
normalized_ids = [
stream.id if hasattr(stream, "id") else stream
for stream in stream_ids
for stream in streams
]
print(normalized_ids)
# Get current mapping of stream_id -> ChannelStream
current_links = {

View file

@ -23,7 +23,6 @@
"@tanstack/react-table": "^8.21.2",
"allotment": "^1.20.3",
"axios": "^1.8.2",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"formik": "^2.4.6",
"hls.js": "^1.5.20",
@ -39,7 +38,6 @@
"react-virtualized": "^9.22.6",
"react-virtualized-auto-sizer": "^1.0.26",
"react-window": "^1.8.11",
"recharts": "^2.15.1",
"video.js": "^8.21.0",
"yup": "^1.6.1",
"zustand": "^5.0.3"
@ -1102,25 +1100,29 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/d3-ease": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/d3-color": "*"
}
@ -1129,13 +1131,15 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/d3-scale": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/d3-time": "*"
}
@ -1145,6 +1149,7 @@
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/d3-path": "*"
}
@ -1153,13 +1158,15 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/d3-timer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/estree": {
"version": "1.0.6",
@ -1595,6 +1602,7 @@
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"license": "ISC",
"peer": true,
"dependencies": {
"internmap": "1 - 2"
},
@ -1607,6 +1615,7 @@
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=12"
}
@ -1616,6 +1625,7 @@
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=12"
}
@ -1625,6 +1635,7 @@
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=12"
}
@ -1634,6 +1645,7 @@
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"license": "ISC",
"peer": true,
"dependencies": {
"d3-color": "1 - 3"
},
@ -1646,6 +1658,7 @@
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=12"
}
@ -1655,6 +1668,7 @@
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"license": "ISC",
"peer": true,
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
@ -1671,6 +1685,7 @@
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"license": "ISC",
"peer": true,
"dependencies": {
"d3-path": "^3.1.0"
},
@ -1683,6 +1698,7 @@
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"license": "ISC",
"peer": true,
"dependencies": {
"d3-array": "2 - 3"
},
@ -1695,6 +1711,7 @@
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"license": "ISC",
"peer": true,
"dependencies": {
"d3-time": "1 - 3"
},
@ -1707,6 +1724,7 @@
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=12"
}
@ -1738,7 +1756,8 @@
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/deep-is": {
"version": "0.1.4",
@ -2108,6 +2127,7 @@
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
"integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.0.0"
}
@ -2457,6 +2477,7 @@
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=12"
}
@ -3206,7 +3227,8 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
@ -3332,6 +3354,7 @@
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
"integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"fast-equals": "^5.0.1",
"prop-types": "^15.8.1",
@ -3456,6 +3479,7 @@
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.1.tgz",
"integrity": "sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"clsx": "^2.0.0",
"eventemitter3": "^4.0.1",
@ -3479,6 +3503,7 @@
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
"license": "MIT",
"peer": true,
"dependencies": {
"decimal.js-light": "^2.4.1"
}
@ -3487,7 +3512,8 @@
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
@ -3683,7 +3709,8 @@
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/tiny-warning": {
"version": "1.0.3",
@ -3837,6 +3864,7 @@
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
"integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
"license": "MIT AND ISC",
"peer": true,
"dependencies": {
"@types/d3-array": "^3.0.3",
"@types/d3-ease": "^3.0.0",

View file

@ -25,7 +25,6 @@
"@tanstack/react-table": "^8.21.2",
"allotment": "^1.20.3",
"axios": "^1.8.2",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"formik": "^2.4.6",
"hls.js": "^1.5.20",
@ -41,7 +40,6 @@
"react-virtualized": "^9.22.6",
"react-virtualized-auto-sizer": "^1.0.26",
"react-window": "^1.8.11",
"recharts": "^2.15.1",
"video.js": "^8.21.0",
"yup": "^1.6.1",
"zustand": "^5.0.3"

View file

@ -450,9 +450,15 @@ export default class API {
}
}
static async getStreams() {
static async getStreams(ids = null) {
try {
const response = await request(`${host}/api/channels/streams/`);
const params = new URLSearchParams();
if (ids) {
params.append('ids', ids.join(','));
}
const response = await request(
`${host}/api/channels/streams/?${params.toString()}`
);
return response;
} catch (e) {
@ -460,6 +466,18 @@ export default class API {
}
}
static async getChannelStreams(id) {
try {
const response = await request(
`${host}/api/channels/channels/${id}/streams/`
);
return response;
} catch (e) {
errorNotification('Failed to retrieve channel streams', e);
}
}
static async queryStreams(params) {
try {
const response = await request(

View file

@ -139,7 +139,7 @@ const Channel = ({ channel = null, isOpen, onClose }) => {
response = await API.updateChannel({
id: channel.id,
...otherValues,
stream_ids: channelStreams.map((stream) => stream.id),
streams: channelStreams.map((stream) => stream.id),
});
}
} else {
@ -147,14 +147,14 @@ const Channel = ({ channel = null, isOpen, onClose }) => {
response = await API.updateChannel({
id: channel.id,
...formattedValues,
stream_ids: channelStreams.map((stream) => stream.id),
streams: channelStreams.map((stream) => stream.id),
});
}
} else {
// New channel creation - use the standard method
response = await API.addChannel({
...formattedValues,
stream_ids: channelStreams.map((stream) => stream.id),
streams: channelStreams.map((stream) => stream.id),
});
}
} catch (error) {

View file

@ -133,7 +133,7 @@ const ChannelStreams = ({ channel, isExpanded }) => {
const newStreamList = data.filter((s) => s.id !== stream.id);
await API.updateChannel({
...channel,
stream_ids: newStreamList.map((s) => s.id),
streams: newStreamList.map((s) => s.id),
});
await API.requeryChannels();
};
@ -203,7 +203,7 @@ const ChannelStreams = ({ channel, isExpanded }) => {
const { streams: _, ...channelUpdate } = channel;
API.updateChannel({
...channelUpdate,
stream_ids: retval.map((row) => row.id),
streams: retval.map((row) => row.id),
}).then(() => {
API.requeryChannels();
});

View file

@ -198,6 +198,7 @@ const ChannelsTable = ({}) => {
const sorting = useChannelsTableStore((s) => s.sorting);
const setSorting = useChannelsTableStore((s) => s.setSorting);
const totalCount = useChannelsTableStore((s) => s.totalCount);
const setChannelStreams = useChannelsTableStore((s) => s.setChannelStreams);
// store/channels
const channels = useChannelsStore((s) => s.channels);
@ -264,6 +265,7 @@ const ChannelsTable = ({}) => {
const params = new URLSearchParams();
params.append('page', pagination.pageIndex + 1);
params.append('page_size', pagination.pageSize);
params.append('include_streams', 'true');
// Apply sorting
if (sorting.length > 0) {
@ -277,8 +279,10 @@ const ChannelsTable = ({}) => {
if (value) params.append(key, value);
});
const results = await API.queryChannels(params);
const ids = await API.getAllChannelIds(params);
const [results, ids] = await Promise.all([
await API.queryChannels(params),
await API.getAllChannelIds(params),
]);
setTablePrefs({
pageSize: pagination.pageSize,

View file

@ -52,7 +52,7 @@ const StreamRowActions = ({
handleWatchStream,
selectedChannelIds,
}) => {
const channelSelectionStreams = useChannelsTableStore(
const channelSelectionStreamIds = useChannelsTableStore(
(state) =>
state.channels.find((chan) => chan.id === selectedChannelIds[0])?.streams
);
@ -71,12 +71,8 @@ const StreamRowActions = ({
const addStreamToChannel = async () => {
await API.updateChannel({
id: selectedChannelIds[0],
stream_ids: [
...new Set(
channelSelectionStreams
.map((stream) => stream.id)
.concat([row.original.id])
),
streams: [
...new Set(channelSelectionStreamIds.concat([row.original.id])),
],
});
await API.requeryChannels();
@ -105,10 +101,8 @@ const StreamRowActions = ({
style={{ background: 'none' }}
disabled={
selectedChannelIds.length !== 1 ||
(channelSelectionStreams &&
channelSelectionStreams
.map((stream) => stream.id)
.includes(row.original.id))
(channelSelectionStreamIds &&
channelSelectionStreamIds.includes(row.original.id))
}
>
<ListPlus size="18" fontSize="small" />
@ -198,7 +192,7 @@ const StreamsTable = ({}) => {
const channelGroups = useChannelsStore((s) => s.channelGroups);
const selectedChannelIds = useChannelsTableStore((s) => s.selectedChannelIds);
const fetchLogos = useChannelsStore((s) => s.fetchLogos);
const channelSelectionStreams = useChannelsTableStore(
const channelSelectionStreamIds = useChannelsTableStore(
(state) =>
state.channels.find((chan) => chan.id === selectedChannelIds[0])?.streams
);
@ -322,11 +316,16 @@ const StreamsTable = ({}) => {
});
try {
const result = await API.queryStreams(params);
const ids = await API.getAllStreamIds(params);
const [result, ids, groups] = await Promise.all([
API.queryStreams(params),
API.getAllStreamIds(params),
API.getStreamGroups(),
]);
setAllRowIds(ids);
setData(result.results);
setPageCount(Math.ceil(result.count / pagination.pageSize));
setGroupOptions(groups);
// Calculate the starting and ending item indexes
const startItem = pagination.pageIndex * pagination.pageSize + 1; // +1 to start from 1, not 0
@ -345,9 +344,6 @@ const StreamsTable = ({}) => {
console.error('Error fetching data:', error);
}
const groups = await API.getStreamGroups();
setGroupOptions(groups);
setIsLoading(false);
}, [pagination, sorting, debouncedFilters]);
@ -388,12 +384,8 @@ const StreamsTable = ({}) => {
const addStreamsToChannel = async () => {
await API.updateChannel({
id: selectedChannelIds[0],
stream_ids: [
...new Set(
channelSelectionStreams
.map((stream) => stream.id)
.concat(selectedStreamIds)
),
streams: [
...new Set(channelSelectionStreamIds.concat(selectedStreamIds)),
],
});
await API.requeryChannels();
@ -540,12 +532,11 @@ const StreamsTable = ({}) => {
deleteStream={deleteStream}
handleWatchStream={handleWatchStream}
selectedChannelIds={selectedChannelIds}
channelSelectionStreams={channelSelectionStreams}
/>
);
}
},
[selectedChannelIds, channelSelectionStreams]
[selectedChannelIds, channelSelectionStreamIds]
);
const table = useTable({