handle download queue search

This commit is contained in:
Simon
2025-07-10 11:29:50 +07:00
parent 83404628e6
commit ffd3bab948
4 changed files with 35 additions and 4 deletions

View File

@@ -47,6 +47,7 @@ class DownloadListQuerySerializer(
) )
channel = serializers.CharField(required=False, help_text="channel ID") channel = serializers.CharField(required=False, help_text="channel ID")
page = serializers.IntegerField(required=False) page = serializers.IntegerField(required=False)
q = serializers.CharField(required=False, help_text="Search Query")
class DownloadListQueueDeleteQuerySerializer(serializers.Serializer): class DownloadListQueueDeleteQuerySerializer(serializers.Serializer):

View File

@@ -73,6 +73,10 @@ class DownloadApiListView(ApiBaseView):
{"term": {"vid_type": {"value": vid_type_filter}}} {"term": {"vid_type": {"value": vid_type_filter}}}
) )
search_query = validated_data.get("q")
if search_query:
must_list.append({"match_phrase_prefix": {"title": search_query}})
self.data["query"] = {"bool": {"must": must_list}} self.data["query"] = {"bool": {"must": must_list}}
self.get_document_list(request) self.get_document_list(request)

View File

@@ -6,12 +6,14 @@ const loadDownloadQueue = async (
channelId: string | null, channelId: string | null,
vid_type: string | null, vid_type: string | null,
showIgnored: boolean, showIgnored: boolean,
search: string,
) => { ) => {
const searchParams = new URLSearchParams(); const searchParams = new URLSearchParams();
if (page) searchParams.append('page', page.toString()); if (page) searchParams.append('page', page.toString());
if (channelId) searchParams.append('channel', channelId); if (channelId) searchParams.append('channel', channelId);
if (vid_type) searchParams.append('vid_type', vid_type); if (vid_type) searchParams.append('vid_type', vid_type);
if (search) searchParams.append('q', encodeURIComponent(search));
searchParams.append('filter', showIgnored ? 'ignore' : 'pending'); searchParams.append('filter', showIgnored ? 'ignore' : 'pending');
const endpoint = `/api/download/${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const endpoint = `/api/download/${searchParams.toString() ? `?${searchParams.toString()}` : ''}`;

View File

@@ -4,6 +4,7 @@ import iconAdd from '/img/icon-add.svg';
import iconSubstract from '/img/icon-substract.svg'; import iconSubstract from '/img/icon-substract.svg';
import iconGridView from '/img/icon-gridview.svg'; import iconGridView from '/img/icon-gridview.svg';
import iconListView from '/img/icon-listview.svg'; import iconListView from '/img/icon-listview.svg';
import iconSearch from '/img/icon-search.svg';
import { Fragment, useEffect, useState } from 'react'; import { Fragment, useEffect, useState } from 'react';
import { useOutletContext, useSearchParams } from 'react-router-dom'; import { useOutletContext, useSearchParams } from 'react-router-dom';
import { ConfigType } from './Home'; import { ConfigType } from './Home';
@@ -64,6 +65,8 @@ const Download = () => {
const [addAsAutoStart, setAddAsAutoStart] = useState(false); const [addAsAutoStart, setAddAsAutoStart] = useState(false);
const [addAsFlat, setAddAsFlat] = useState(false); const [addAsFlat, setAddAsFlat] = useState(false);
const [showQueueActions, setShowQueueActions] = useState(false); const [showQueueActions, setShowQueueActions] = useState(false);
const [showSearchInput, setShowSearchInput] = useState(false);
const [searchInput, setSearchInput] = useState('');
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [downloadPending, setDownloadPending] = useState(false); const [downloadPending, setDownloadPending] = useState(false);
const [rescanPending, setRescanPending] = useState(false); const [rescanPending, setRescanPending] = useState(false);
@@ -118,6 +121,7 @@ const Download = () => {
channelFilterFromUrl, channelFilterFromUrl,
vidTypeFilterFromUrl, vidTypeFilterFromUrl,
showIgnored, showIgnored,
searchInput,
); );
const { data: channelResponseData } = videosResponse ?? {}; const { data: channelResponseData } = videosResponse ?? {};
const videoCount = channelResponseData?.paginate?.total_hits; const videoCount = channelResponseData?.paginate?.total_hits;
@@ -136,7 +140,7 @@ const Download = () => {
useEffect(() => { useEffect(() => {
setRefresh(true); setRefresh(true);
}, [channelFilterFromUrl, vidTypeFilterFromUrl, currentPage, showIgnored]); }, [channelFilterFromUrl, vidTypeFilterFromUrl, currentPage, showIgnored, searchInput]);
useEffect(() => { useEffect(() => {
(async () => { (async () => {
@@ -156,6 +160,11 @@ const Download = () => {
setRefresh(true); setRefresh(true);
}; };
const handleShowSearchInput = () => {
if (showSearchInput) setSearchInput('');
setShowSearchInput(!showSearchInput);
};
return ( return (
<> <>
<title>TA | Downloads</title> <title>TA | Downloads</title>
@@ -309,9 +318,21 @@ const Download = () => {
</div> </div>
</div> </div>
<div className="view-icons"> <div className="view-icons">
<Button onClick={() => setShowQueueActions(!showQueueActions)}> {showSearchInput && (
{showQueueActions ? 'Hide Actions' : 'Show Actions'} <input
</Button> type="text"
placeholder="Search..."
value={searchInput}
onChange={e => setSearchInput(e.target.value)}
autoFocus
/>
)}
<img
src={iconSearch}
alt="search-icon"
title="Search"
onClick={handleShowSearchInput}
/>
<select <select
name="vid_type_filter" name="vid_type_filter"
id="vid_type_filter" id="vid_type_filter"
@@ -363,6 +384,9 @@ const Download = () => {
})} })}
</select> </select>
)} )}
<Button onClick={() => setShowQueueActions(!showQueueActions)}>
{showQueueActions ? 'Hide Actions' : 'Show Actions'}
</Button>
{isGridView && ( {isGridView && (
<div className="grid-count"> <div className="grid-count">