mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-20 16:51:10 +00:00
channel streams optimizations, bug fixes in tables, parallel api calls for paginated data fetching
This commit is contained in:
parent
1d63075660
commit
8fe83fd9ee
10 changed files with 129 additions and 64 deletions
|
|
@ -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'),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
52
frontend/package-lock.json
generated
52
frontend/package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue