diff --git a/backend/download/serializers.py b/backend/download/serializers.py index 5ab3eca8..4b818fb5 100644 --- a/backend/download/serializers.py +++ b/backend/download/serializers.py @@ -78,6 +78,22 @@ class AddToDownloadQuerySerializer(serializers.Serializer): autostart = serializers.BooleanField(required=False) +class BulkUpdateDowloadQuerySerializer(serializers.Serializer): + """serialize bulk update query""" + + filter = serializers.ChoiceField(choices=["pending", "ignore", "priority"]) + channel = serializers.CharField(required=False) + vid_type = serializers.ChoiceField( + choices=VideoTypeEnum.values_known(), required=False + ) + + +class BulkUpdateDowloadDataSerializer(serializers.Serializer): + """serialize data""" + + status = serializers.ChoiceField(choices=["pending", "ignore", "priority"]) + + class DownloadQueueItemUpdateSerializer(serializers.Serializer): """update single download queue item""" diff --git a/backend/download/src/queue.py b/backend/download/src/queue.py index 3b21fc68..000dae57 100644 --- a/backend/download/src/queue.py +++ b/backend/download/src/queue.py @@ -114,6 +114,30 @@ class PendingInteract: path = "ta_download/_delete_by_query?refresh=true" _, _ = ElasticWrap(path).post(data=data) + def update_bulk( + self, channel_id: str | None, vid_type: str | None, new_status: str + ): + """update status in bulk""" + must_list = [{"term": {"status": {"value": self.status}}}] + if channel_id: + must_list.append({"term": {"channel_id": {"value": channel_id}}}) + + if vid_type: + must_list.append({"term": {"vid_type": {"value": vid_type}}}) + + data = { + "query": {"bool": {"must": must_list}}, + "script": { + "source": f"ctx._source.status = '{new_status}'", + "lang": "painless", + }, + } + print(data) + path = "ta_download/_update_by_query?refresh=true" + response, status_code = ElasticWrap(path).post(data) + print(status_code) + print(response) + def update_status(self): """update status of pending item""" if self.status == "priority": diff --git a/backend/download/views.py b/backend/download/views.py index b3cec1a9..0d66faca 100644 --- a/backend/download/views.py +++ b/backend/download/views.py @@ -8,6 +8,8 @@ from common.views_base import AdminOnly, ApiBaseView from download.serializers import ( AddToDownloadListSerializer, AddToDownloadQuerySerializer, + BulkUpdateDowloadDataSerializer, + BulkUpdateDowloadQuerySerializer, DownloadAggsSerializer, DownloadItemSerializer, DownloadListQuerySerializer, @@ -120,6 +122,38 @@ class DownloadApiListView(ApiBaseView): return Response(response_serializer.data) + @staticmethod + @extend_schema( + request=BulkUpdateDowloadDataSerializer(), + parameters=[BulkUpdateDowloadQuerySerializer()], + responses={204: OpenApiResponse(description="Status updated")}, + ) + def patch(request): + """bulk update status""" + data_serializer = BulkUpdateDowloadDataSerializer(data=request.data) + data_serializer.is_valid(raise_exception=True) + validated_data = data_serializer.validated_data + + new_status = validated_data["status"] + + query_serializer = BulkUpdateDowloadQuerySerializer( + data=request.query_params + ) + query_serializer.is_valid(raise_exception=True) + validated_query = query_serializer.validated_data + status_filter = validated_query.get("filter") + channel = validated_query.get("channel") + vid_type = validated_query.get("vid_type") + + PendingInteract(status=status_filter).update_bulk( + channel_id=channel, vid_type=vid_type, new_status=new_status + ) + + if new_status == "priority": + download_pending.delay(auto_only=True) + + return Response(status=204) + @extend_schema( parameters=[DownloadListQueueDeleteQuerySerializer()], responses={ diff --git a/frontend/src/api/actions/updateDownloadQueueByFilter.ts b/frontend/src/api/actions/updateDownloadQueueByFilter.ts new file mode 100644 index 00000000..1410360e --- /dev/null +++ b/frontend/src/api/actions/updateDownloadQueueByFilter.ts @@ -0,0 +1,23 @@ +import APIClient from '../../functions/APIClient'; + +type FilterType = 'ignore' | 'pending'; +export type DownloadQueueStatus = 'ignore' | 'pending' | 'priority'; + +const updateDownloadQueueByFilter = async ( + filter: FilterType, + channel: string | null, + vid_type: string | null, + status: DownloadQueueStatus, +) => { + const searchParams = new URLSearchParams(); + if (filter) searchParams.append('filter', filter); + if (channel) searchParams.append('channel', channel); + if (vid_type) searchParams.append('vid_type', vid_type); + + return APIClient(`/api/download/?${searchParams.toString()}`, { + method: 'PATCH', + body: { status: status }, + }); +}; + +export default updateDownloadQueueByFilter; diff --git a/frontend/src/functions/APIClient.ts b/frontend/src/functions/APIClient.ts index c0656a63..5b464f3d 100644 --- a/frontend/src/functions/APIClient.ts +++ b/frontend/src/functions/APIClient.ts @@ -6,7 +6,7 @@ import getCookie from './getCookie'; import Routes from '../configuration/routes/RouteList'; export interface ApiClientOptions extends Omit { - method?: 'GET' | 'POST' | 'PUT' | 'DELETE'; + method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; body?: Record | string; } diff --git a/frontend/src/pages/Download.tsx b/frontend/src/pages/Download.tsx index a4a4388b..3cd98d0b 100644 --- a/frontend/src/pages/Download.tsx +++ b/frontend/src/pages/Download.tsx @@ -22,6 +22,9 @@ import { useUserConfigStore } from '../stores/UserConfigStore'; import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig'; import { ApiResponseType } from '../functions/APIClient'; import deleteDownloadQueueByFilter from '../api/actions/deleteDownloadQueueByFilter'; +import updateDownloadQueueByFilter, { + DownloadQueueStatus, +} from '../api/actions/updateDownloadQueueByFilter'; type Download = { auto_start: boolean; @@ -141,6 +144,16 @@ const Download = () => { })(); }, [lastVideoCount, showIgnored]); + const handleBulkStatusUpdate = async (status: DownloadQueueStatus) => { + await updateDownloadQueueByFilter( + showIgnoredFilter, + channelFilterFromUrl, + vidTypeFilterFromUrl, + status, + ); + setRefresh(true); + }; + return ( <> TA | Downloads @@ -397,6 +410,18 @@ const Download = () => { )}

+
+ {showIgnored ? ( +
+ +
+ ) : ( +
+ + +
+ )} +
{showDeleteConfirm ? ( <> @@ -416,7 +441,7 @@ const Download = () => { ) : ( - + )}