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