diff --git a/app/library/downloads/queue_manager.py b/app/library/downloads/queue_manager.py index 807b3ffa..c654f488 100644 --- a/app/library/downloads/queue_manager.py +++ b/app/library/downloads/queue_manager.py @@ -300,6 +300,7 @@ class DownloadQueue(metaclass=Singleton): """ status: dict[str, str] = {} + deleted_ids: list[str] = [] for id in ids: try: @@ -349,6 +350,7 @@ class DownloadQueue(metaclass=Singleton): LOG.error(f"Unable to remove '{itemRef}' local file '{filename}'. {e!s}") await self.done.delete(id) + deleted_ids.append(id) _status: str = "Removed" if removed_files > 0 else "Cleared" self._notify.emit( @@ -365,6 +367,9 @@ class DownloadQueue(metaclass=Singleton): LOG.info(msg=msg) status[id] = "ok" + if deleted_ids: + await self.done._connection.flush() + return status async def get(self, mode: str = "all") -> dict[str, list[dict[str, ItemDTO]]]: diff --git a/app/routes/api/history.py b/app/routes/api/history.py index e29d7fc8..2e161651 100644 --- a/app/routes/api/history.py +++ b/app/routes/api/history.py @@ -215,7 +215,11 @@ async def items_delete(request: Request, queue: DownloadQueue, encoder: Encoder) page += 1 if not items_to_delete: - return web.json_response(data={"error": "No items matched the filter."}, status=web.HTTPBadRequest.status_code) + return web.json_response( + data={"items": {}, "deleted": 0}, + status=web.HTTPOk.status_code, + dumps=encoder.encode, + ) return web.json_response( data={ diff --git a/app/tests/test_download.py b/app/tests/test_download.py index c1601a7f..76166f7d 100644 --- a/app/tests/test_download.py +++ b/app/tests/test_download.py @@ -1336,6 +1336,32 @@ class TestQueueManager: item.close.assert_awaited_once() assert status[item.info._id] == "ok", "Regular running cancel should still report success" + @pytest.mark.asyncio + async def test_clear_flushes_history_deletes_before_returning(self) -> None: + queue_manager = object.__new__(DownloadQueue) + queue_manager.config = Mock(remove_files=False, download_path="/tmp") + queue_manager._notify = Mock() + + item = Mock() + item.info = make_item(id="done-id", title="Finished clip") + item.info._id = "done-id" + item.info.status = "finished" + item.info.filename = "clip.mp4" + item.info.folder = "" + + done_store = Mock() + done_store.get = AsyncMock(return_value=item) + done_store.delete = AsyncMock() + done_store._connection = Mock() + done_store._connection.flush = AsyncMock() + queue_manager.done = done_store + + status = await DownloadQueue.clear(queue_manager, [item.info._id], remove_file=False) + + done_store.delete.assert_awaited_once_with(item.info._id) + done_store._connection.flush.assert_awaited_once() + assert status[item.info._id] == "ok", "Clear should still report success after flushing deletes" + class TestPoolManager: @pytest.mark.asyncio