- 修复Redis认证 (配置密码) - 启动Python管理后台 (端口9531, 15个功能开关) - 统一版本号 0.2.7 - 更新docker-compose.yml (镜像版本/Redis URL/Admin服务)
204 lines
6.8 KiB
Python
204 lines
6.8 KiB
Python
"""
|
||
阿里云盘回收站清理模块 v1.0.0
|
||
将文件移入回收站(非直接删除),支持批量操作。
|
||
"""
|
||
|
||
import logging
|
||
from typing import List, Dict
|
||
|
||
import requests
|
||
|
||
from .credential import AliyunCredentialManager, API_HOST
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# ─── API 端点 ──────────────────────────────────────────────
|
||
|
||
# 批量操作(v4)
|
||
BATCH_URL = f"{API_HOST}/adrive/v4/batch"
|
||
|
||
# 默认请求头
|
||
DEFAULT_HEADERS = {
|
||
"User-Agent": (
|
||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||
"Chrome/135.0.0.0 Safari/537.36"
|
||
),
|
||
"Accept": "application/json, text/plain, */*",
|
||
"Content-Type": "application/json",
|
||
"Referer": "https://aliyundrive.com",
|
||
}
|
||
|
||
|
||
class AliyunCleanup:
|
||
"""
|
||
阿里云盘回收站清理
|
||
|
||
将文件移入回收站(放入回收站,非永久删除)。
|
||
使用 v4 批量接口,支持一次清理多个文件。
|
||
|
||
用法:
|
||
credential = AliyunCredentialManager(refresh_token="xxx")
|
||
cleanup = AliyunCleanup(credential, drive_id="12345")
|
||
result = cleanup.delete_files(["file_id_1", "file_id_2"])
|
||
"""
|
||
|
||
def __init__(
|
||
self,
|
||
credential: AliyunCredentialManager,
|
||
drive_id: str = "",
|
||
request_timeout: int = 30,
|
||
):
|
||
self.credential = credential
|
||
self.drive_id = drive_id or credential.get_drive_id()
|
||
self.request_timeout = request_timeout
|
||
self._session = requests.Session()
|
||
self._session.headers.update(DEFAULT_HEADERS)
|
||
|
||
# ─── 公开 API ──────────────────────────────────────────
|
||
|
||
def delete_files(self, file_ids: List[str]) -> Dict:
|
||
"""
|
||
将指定文件移入回收站(批量)。
|
||
|
||
Args:
|
||
file_ids: 要删除的文件 ID 列表
|
||
|
||
Returns:
|
||
{
|
||
"success": True/False,
|
||
"deleted_count": 成功删除数量,
|
||
"total_count": 总文件数,
|
||
"failed_ids": 失败的文件 ID 列表,
|
||
"error": None or "错误信息",
|
||
}
|
||
|
||
实现:
|
||
POST /adrive/v4/batch
|
||
{
|
||
"requests": [
|
||
{
|
||
"url": "/recyclebin/trash",
|
||
"body": {"file_id": "...", "drive_id": "..."},
|
||
"headers": {"Content-Type": "application/json"},
|
||
"id": "...",
|
||
"method": "POST"
|
||
}
|
||
],
|
||
"resource": "file"
|
||
}
|
||
"""
|
||
if not file_ids:
|
||
return self._error("文件 ID 列表为空")
|
||
|
||
drive_id = self.drive_id
|
||
if not drive_id:
|
||
drive_id = self.credential.get_drive_id()
|
||
if not drive_id:
|
||
return self._error("缺少 drive_id,无法执行删除操作")
|
||
|
||
# 构建批量请求体
|
||
requests_list = []
|
||
for fid in file_ids:
|
||
requests_list.append({
|
||
"url": "/recyclebin/trash",
|
||
"body": {
|
||
"drive_id": drive_id,
|
||
"file_id": fid,
|
||
},
|
||
"headers": {"Content-Type": "application/json"},
|
||
"id": fid,
|
||
"method": "POST",
|
||
})
|
||
|
||
try:
|
||
headers = self.credential.get_headers()
|
||
|
||
resp = self._session.post(
|
||
BATCH_URL,
|
||
json={"requests": requests_list, "resource": "file"},
|
||
headers=headers,
|
||
timeout=self.request_timeout,
|
||
)
|
||
data = resp.json()
|
||
|
||
if resp.status_code != 200:
|
||
logger.error(
|
||
f"[AliyunCleanup] 批量删除失败: "
|
||
f"HTTP {resp.status_code}, {data}"
|
||
)
|
||
return self._error(f"HTTP {resp.status_code}")
|
||
|
||
code = data.get("code", "")
|
||
if code:
|
||
logger.error(
|
||
f"[AliyunCleanup] 批量删除 API 错误: "
|
||
f"code={code}, message={data.get('message', '')}"
|
||
)
|
||
return self._error(data.get("message", f"API code={code}"))
|
||
|
||
# 统计结果
|
||
responses = data.get("responses", [])
|
||
success_ids = []
|
||
failed_ids = []
|
||
|
||
for item in responses:
|
||
status = item.get("status", 0)
|
||
fid = item.get("id", "")
|
||
if status in (200, 201, 202):
|
||
success_ids.append(fid)
|
||
else:
|
||
logger.warning(
|
||
f"[AliyunCleanup] 删除文件失败: "
|
||
f"id={fid}, status={status}, body={item.get('body', {})}"
|
||
)
|
||
failed_ids.append(fid)
|
||
|
||
logger.info(
|
||
f"[AliyunCleanup] 删除完成: "
|
||
f"成功={len(success_ids)}, 失败={len(failed_ids)}, 总计={len(file_ids)}"
|
||
)
|
||
|
||
return {
|
||
"success": len(failed_ids) == 0,
|
||
"deleted_count": len(success_ids),
|
||
"total_count": len(file_ids),
|
||
"success_ids": success_ids,
|
||
"failed_ids": failed_ids,
|
||
"error": None,
|
||
}
|
||
|
||
except requests.RequestException as e:
|
||
logger.error(f"[AliyunCleanup] 批量删除网络异常: {e}")
|
||
return self._error(str(e))
|
||
except Exception as e:
|
||
logger.exception(f"[AliyunCleanup] 批量删除异常: {e}")
|
||
return self._error(str(e))
|
||
|
||
def empty_recycle_bin(self) -> Dict:
|
||
"""
|
||
清空回收站(永久删除回收站中的所有文件)。
|
||
|
||
NOTE: 阿里云盘 API 目前不直接支持清空回收站,
|
||
此方法作为占位,需要逐个文件 ID 调用 delete_files。
|
||
实际使用请先 list 回收站内容再调用 delete_files。
|
||
|
||
Returns:
|
||
{"success": False, "error": "清空回收站需要通过 list + delete 两步完成"}
|
||
"""
|
||
logger.warning("[AliyunCleanup] 清空回收站 API 暂未实现,需要 list+delete 两步")
|
||
return self._error("清空回收站需要通过列出回收站内容 + 逐个删除两步完成,尚未实现")
|
||
|
||
# ─── 工具方法 ──────────────────────────────────────────
|
||
|
||
def _error(self, message: str) -> Dict:
|
||
"""构造错误返回"""
|
||
return {
|
||
"success": False,
|
||
"deleted_count": 0,
|
||
"total_count": 0,
|
||
"success_ids": [],
|
||
"failed_ids": [],
|
||
"error": message,
|
||
}
|