refactor: switch api/history to be fully async.

This commit is contained in:
arabcoders 2026-01-25 21:34:26 +03:00
parent 7d6dc7ae76
commit fe6fe42a65
3 changed files with 924 additions and 364 deletions

1200
API.md

File diff suppressed because it is too large Load diff

View file

@ -385,34 +385,67 @@ async def items_add(request: Request, queue: DownloadQueue, encoder: Encoder) ->
Returns: Returns:
Response: The response object. Response: The response object.
Query Parameters:
sync (bool): If true, wait for all items to be processed synchronously. Default: false
""" """
data = await request.json() data = await request.json()
if isinstance(data, dict): if isinstance(data, dict):
data = [data] data = [data]
items = [] items: list[Item] = []
for item in data: for item in data:
try: try:
items.append(Item.format(item)) items.append(Item.format(item))
except ValueError as e: except ValueError as e:
return web.json_response(data={"error": str(e), "data": item}, status=web.HTTPBadRequest.status_code) return web.json_response(data={"error": str(e), "data": item}, status=web.HTTPBadRequest.status_code)
status: list[dict] = await asyncio.wait_for( if "true" == request.query.get("sync", "false").lower():
fut=asyncio.gather(*[queue.add(item=item) for item in items]), status: list[dict] = await asyncio.wait_for(
timeout=None, fut=asyncio.gather(*[queue.add(item=item) for item in items]),
timeout=None,
)
response: list[dict[str, Any]] = []
for i, item in enumerate(items):
it = {"item": item, "status": "ok" == status[i].get("status"), "msg": status[i].get("msg")}
if status[i].get("hidden"):
it["hidden"] = True
response.append(it)
return web.json_response(data=response, status=web.HTTPOk.status_code, dumps=encoder.encode)
from app.library.downloads.utils import handle_task_exception
batch_id: str = f"batch_{asyncio.get_running_loop().time():.0f}"
for idx, item in enumerate(items):
if not item.extras:
item.extras = {}
item.extras["batch_id"] = batch_id
item.extras["batch_index"] = idx
item.extras["batch_total"] = len(items)
task = asyncio.create_task(
queue.add(item=item),
name=f"bulk_add_{batch_id}_{idx}",
)
task.add_done_callback(lambda t: handle_task_exception(t, LOG))
return web.json_response(
data={
"status": "accepted",
"message": f"Accepted {len(items)} item(s) for processing",
"batch_id": batch_id,
"count": len(items),
},
status=web.HTTPAccepted.status_code,
dumps=encoder.encode,
) )
response: list[dict[str, Any]] = []
for i, item in enumerate(items):
it = {"item": item, "status": "ok" == status[i].get("status"), "msg": status[i].get("msg")}
if status[i].get("hidden"):
it["hidden"] = True
response.append(it)
return web.json_response(data=response, status=web.HTTPOk.status_code, dumps=encoder.encode)
@route("POST", "api/history/start", "items_start") @route("POST", "api/history/start", "items_start")
async def items_start(request: Request, queue: DownloadQueue, encoder: Encoder) -> Response: async def items_start(request: Request, queue: DownloadQueue, encoder: Encoder) -> Response:

View file

@ -309,7 +309,6 @@ const form = useStorage<item_request>('local_config_v1', {
extras: {}, extras: {},
}) as Ref<item_request> }) as Ref<item_request>
const is_valid_dl_field = (dl_field: string): boolean => { const is_valid_dl_field = (dl_field: string): boolean => {
if (dlFieldsExtra.includes(dl_field)) { if (dlFieldsExtra.includes(dl_field)) {
return true return true
@ -471,19 +470,25 @@ const addDownload = async () => {
let had_errors = false let had_errors = false
data.forEach((item: Record<string, any>) => { if (200 === response.status) {
if (false !== item.status) { data.forEach((item: Record<string, any>) => {
return if (false !== item.status) {
} return
}
had_errors = true had_errors = true
if (item?.hidden) { if (item?.hidden) {
return return
} }
toast.error(`Error: ${item.msg || 'Failed to add download.'}`) toast.error(`Error: ${item.msg || 'Failed to add download.'}`)
}) })
}
if (202 === response.status) {
toast.success(data.message,{ timeout: 2000 })
}
if (false === had_errors) { if (false === had_errors) {
form.value.url = '' form.value.url = ''