diff --git a/task/baidupcs.py b/task/baidupcs.py
index 0136685..4a2b9b7 100644
--- a/task/baidupcs.py
+++ b/task/baidupcs.py
@@ -132,6 +132,9 @@ def leech(self, remote_dir, local_dir, sample_size=0):
self.download_dir(remote_dir, local_dir, sample_size=sample_size)
+ def delete(self, remote_dir):
+ self.api.remove(remote_dir)
+
def remotepath_exists(api, name: str, rd: str, _cache={}) -> bool:
names = _cache.get(rd)
diff --git a/task/models.py b/task/models.py
index c8a4383..685d6d9 100644
--- a/task/models.py
+++ b/task/models.py
@@ -1,7 +1,9 @@
+import shutil
from json import dumps
from json import loads
from os import makedirs
from os import walk
+from os.path import exists
from os.path import getsize
from os.path import join
from pathlib import Path
@@ -361,3 +363,13 @@ def recoverable(self):
# assume task is not recoverable by default to avoid flood requests
return False
+
+ def delete_files(self):
+ if exists(self.sample_path):
+ shutil.rmtree(self.sample_path)
+ if exists(self.data_path):
+ shutil.rmtree(self.data_path)
+
+ def erase(self):
+ self.delete_files()
+ self.delete()
diff --git a/task/tests/test_api.py b/task/tests/test_api.py
index 07bcfa8..d36272f 100644
--- a/task/tests/test_api.py
+++ b/task/tests/test_api.py
@@ -15,31 +15,30 @@ def setUp(self):
self.task = Task.objects.create(
shared_link="https://pan.baidu.com/s/123abc?pwd=def",
)
- self.task.set_files(
- [
- {
- "path": "张楚",
- "is_dir": True,
- "is_file": False,
- "size": 0,
- "md5": None,
- },
- {
- "path": "张楚/孤独的人是可耻的.mp3",
- "is_dir": False,
- "is_file": True,
- "size": 9518361,
- "md5": "6d5bea8001e9db88f8cd8145aaf8cce4",
- },
- {
- "path": "张楚/蚂蚁蚂蚁.mp3",
- "is_dir": False,
- "is_file": True,
- "size": 1234567,
- "md5": "1eec826501e9db88f8cd8145aaf8cce4",
- },
- ],
- )
+ self.remote_files = [
+ {
+ "path": "张楚",
+ "is_dir": True,
+ "is_file": False,
+ "size": 0,
+ "md5": None,
+ },
+ {
+ "path": "张楚/孤独的人是可耻的.mp3",
+ "is_dir": False,
+ "is_file": True,
+ "size": 9518361,
+ "md5": "6d5bea8001e9db88f8cd8145aaf8cce4",
+ },
+ {
+ "path": "张楚/蚂蚁蚂蚁.mp3",
+ "is_dir": False,
+ "is_file": True,
+ "size": 1234567,
+ "md5": "1eec826501e9db88f8cd8145aaf8cce4",
+ },
+ ]
+ self.task.set_files(self.remote_files)
self.task.save()
def test_create_task(self):
@@ -216,7 +215,47 @@ def test_resume_not_failed_task_do_nothing(self):
assert task.message == ""
def test_files(self):
+ response = self.client.get(
+ reverse("task-files", args=[self.task.id]),
+ {},
+ format="json",
+ )
+
+ assert response.json() == self.remote_files
assert self.task.total_files == 2
assert self.task.total_size == 10752928
assert self.task.largest_file == "张楚/孤独的人是可耻的.mp3"
assert self.task.largest_file_size == 9518361
+
+ def test_local_files(self):
+ response = self.client.get(
+ reverse("task-local-files", args=[self.task.id]),
+ {},
+ format="json",
+ )
+
+ assert response.json() == []
+
+ @patch("task.views.get_baidupcs_client")
+ def test_delete_remote_files(self, mock_get_baidupcs_client):
+ mock_get_baidupcs_client.return_value = Mock()
+
+ id = self.task.id
+ response = self.client.delete(reverse("task-files", args=[id]))
+
+ assert response.json() == {str(id): "remote files deleted"}
+
+ def test_delete_local_files(self):
+ id = self.task.id
+ response = self.client.delete(reverse("task-local-files", args=[id]))
+
+ assert response.json() == {str(id): "local files deleted"}
+
+ @patch("task.views.get_baidupcs_client")
+ def test_erase(self, mock_get_baidupcs_client):
+ mock_get_baidupcs_client.return_value = Mock()
+
+ id = self.task.id
+ response = self.client.delete(reverse("task-erase", args=[id]))
+
+ assert response.json() == {str(id): "task deleted"}
diff --git a/task/views.py b/task/views.py
index f81a14e..928a99a 100644
--- a/task/views.py
+++ b/task/views.py
@@ -1,6 +1,7 @@
import logging
from io import BytesIO
+from baidupcs_py.baidupcs import BaiduPCSError
from django.http import HttpResponse
from django_filters import rest_framework as filters
from rest_framework import mixins
@@ -20,6 +21,23 @@
logger = logging.getLogger(__name__)
+def delete_remote_files(
+ task_id,
+ remote_path,
+ success_message,
+ catch_error=True,
+):
+ try:
+ client = get_baidupcs_client()
+ client.delete(remote_path)
+ except Exception as exc:
+ if catch_error:
+ return Response({"error": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
+ else:
+ raise exc
+ return Response({task_id: success_message})
+
+
class TaskViewSet(
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
@@ -32,22 +50,33 @@ class TaskViewSet(
filter_backends = (filters.DjangoFilterBackend,)
filterset_fields = ("shared_link", "shared_id", "status", "failed")
- @action(detail=True)
+ @action(methods=["get", "delete"], detail=True, name="Remote Files")
def files(self, request, pk=None):
task = self.get_object()
- return Response(task.load_files())
+ if request.method == "GET":
+ return Response(task.load_files())
+ if request.method == "DELETE":
+ return delete_remote_files(
+ task.id,
+ task.remote_path,
+ "remote files deleted",
+ )
- @action(detail=True)
+ @action(methods=["get", "delete"], detail=True, name="Local Files")
def local_files(self, request, pk=None):
task = self.get_object()
- return Response(task.list_local_files())
+ if request.method == "GET":
+ return Response(task.list_local_files())
+ if request.method == "DELETE":
+ task.delete_files()
+ return Response({task.id: "local files deleted"})
- @action(detail=True)
+ @action(detail=True, name="Captch Image")
def captcha(self, request, pk=None):
task = self.get_object()
return HttpResponse(BytesIO(task.captcha), content_type="image/jpeg")
- @action(methods=["post"], detail=True)
+ @action(methods=["post"], detail=True, name="Input Captcha Code")
def captcha_code(self, request, pk=None):
serializer = CaptchaCodeSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
@@ -71,7 +100,7 @@ def captcha_code(self, request, pk=None):
return Response({"error": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
return Response(TaskSerializer(task).data)
- @action(methods=["post"], detail=True)
+ @action(methods=["post"], detail=True, name="Approve to download whole files")
def full_download_now(self, request, pk=None):
serializer = FullDownloadNowSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
@@ -80,29 +109,45 @@ def full_download_now(self, request, pk=None):
task.save()
return Response(TaskSerializer(task).data)
- @action(methods=["post"], detail=True)
+ @action(methods=["post"], detail=True, name="Restart task to downloading files")
def restart_downloading(self, request, pk=None):
task = self.get_object()
task.restart_downloading()
return Response({"status": task.status})
- @action(methods=["post"], detail=True)
+ @action(methods=["post"], detail=True, name="Restart task from inited status")
def restart(self, request, pk=None):
task = self.get_object()
task.restart()
return Response({"status": task.status})
- @action(methods=["post"], detail=True)
+ @action(methods=["post"], detail=True, name="Resume failed task")
def resume(self, request, pk=None):
task = self.get_object()
task.schedule_resume()
return Response({"status": task.status})
+ @action(methods=["delete"], detail=True, name="Erase task, remote and local files")
+ def erase(self, request, pk=None):
+ task = self.get_object()
+ task_id = task.id
+ task.erase()
+ message = "task deleted"
+ try:
+ return delete_remote_files(
+ task_id,
+ task.remote_path,
+ message,
+ catch_error=False,
+ )
+ except BaiduPCSError:
+ return Response({task_id: message})
+
def get_serializer(self, *args, **kwargs):
if self.action == "captcha_code":
return CaptchaCodeSerializer(*args, **kwargs)
if self.action == "full_download_now":
return FullDownloadNowSerializer(*args, **kwargs)
- if self.action in ["restart", "restart_downloading", "resume"]:
+ if self.action in ["restart", "restart_downloading", "resume", "erase"]:
return OperationSerializer(*args, **kwargs)
return super().get_serializer(*args, **kwargs)
diff --git a/ui/templates/ui/base.html b/ui/templates/ui/base.html
index b8733dc..6a07813 100644
--- a/ui/templates/ui/base.html
+++ b/ui/templates/ui/base.html
@@ -14,7 +14,7 @@
-
+