Files
admin 83cbfaf03f v0.2.7: 修复Redis连接 + 启动管理后台
- 修复Redis认证 (配置密码)
- 启动Python管理后台 (端口9531, 15个功能开关)
- 统一版本号 0.2.7
- 更新docker-compose.yml (镜像版本/Redis URL/Admin服务)
2026-05-17 02:22:18 +08:00

199 lines
5.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
CloudSearch Transfer — 迅雷网盘清理模块 v1.0.0
提供文件删除和广告过滤功能。
"""
from __future__ import annotations
import logging
from typing import Any, Dict, List
import requests
from .credential import XunleiCredentialManager
logger = logging.getLogger(__name__)
# ─── 迅雷 API ─────────────────────────────────────────────────────────
XUNLEI_PAN_API = "https://api-pan.xunlei.com"
class XunleiCleanup:
"""迅雷网盘文件清理器。
提供批量删除文件和广告文件过滤功能。
Attributes:
credential: 迅雷凭证管理器。
session: 复用的 requests.Session。
timeout: HTTP 请求超时秒数。
"""
def __init__(
self,
credential: XunleiCredentialManager,
timeout: int = 30,
) -> None:
"""初始化清理器。
Args:
credential: 有效的迅雷凭证管理器。
timeout: HTTP 请求超时秒数。
"""
self.credential: XunleiCredentialManager = credential
self.timeout: int = timeout
self.session: requests.Session = requests.Session()
def delete_files(self, file_ids: List[str]) -> bool:
"""批量删除文件。
POST /drive/v1/files:batchDelete
Body: {
"ids": ["<fid1>", "<fid2>", ...],
"space": ""
}
Args:
file_ids: 要删除的文件 ID 列表。
Returns:
True 表示删除请求已提交成功False 表示失败。
Raises:
RuntimeError: HTTP 请求错误。
"""
if not file_ids:
logger.warning("[XunleiCleanup] delete_files called with empty list")
return True
url: str = f"{XUNLEI_PAN_API}/drive/v1/files:batchDelete"
body: Dict[str, Any] = {
"ids": file_ids,
"space": "",
}
headers = self.credential.get_headers()
headers.setdefault("Content-Type", "application/json")
logger.info("[XunleiCleanup] Deleting %d files: %s", len(file_ids), file_ids)
try:
resp = self.session.post(
url, json=body, headers=headers, timeout=self.timeout
)
resp.raise_for_status()
except requests.RequestException as exc:
raise RuntimeError(f"删除文件失败: {exc}") from exc
data: Dict[str, Any] = resp.json()
errcode = data.get("errcode", data.get("error_code", 0))
if errcode != 0:
logger.error(
"[XunleiCleanup] Delete returned error: errcode=%s, message=%s",
errcode,
data.get("message", data.get("error", "")),
)
return False
logger.info("[XunleiCleanup] Delete succeeded for %d files", len(file_ids))
return True
def delete_files_permanent(self, file_ids: List[str]) -> bool:
"""彻底删除文件。
迅雷的 batchDelete 默认为彻底删除(与回收站不同),
此方法与 delete_files 行为一致。
Args:
file_ids: 要彻底删除的文件 ID 列表。
Returns:
True 表示删除请求已提交成功。
"""
return self.delete_files(file_ids)
@staticmethod
def filter_ads(
files: List[Dict[str, Any]],
banned_keywords: List[str],
) -> List[Dict[str, Any]]:
"""按关键词过滤文件列表中的广告文件。
遍历文件列表,剔除文件名中包含任一 banned_keywords 的文件。
匹配方式:不区分大小写的子串匹配。
Args:
files: 文件信息字典列表,每个字典需包含 "name""file_name" 字段。
banned_keywords: 被禁关键词列表(匹配不区分大小写)。
Returns:
过滤后的文件信息列表。
"""
if not banned_keywords:
return files
filtered: List[Dict[str, Any]] = []
removed_count: int = 0
for f in files:
name: str = f.get("name", f.get("file_name", ""))
name_lower: str = str(name).lower()
if any(keyword.lower() in name_lower for keyword in banned_keywords):
logger.info("[XunleiCleanup] Filtered ad file: '%s'", name)
removed_count += 1
continue
filtered.append(f)
if removed_count > 0:
logger.info(
"[XunleiCleanup] Ad filter removed %d/%d files",
removed_count,
len(files),
)
return filtered
@staticmethod
def filter_ad_ids(
file_ids: List[str],
file_names: List[str],
banned_keywords: List[str],
) -> List[str]:
"""按关键词过滤文件 ID 列表。
根据 file_names 判断是否为广告,返回对应的 file_ids。
Args:
file_ids: 文件 ID 列表。
file_names: 与 file_ids 一一对应的文件名列表。
banned_keywords: 被禁关键词列表。
Returns:
过滤后的 file_ids 列表。
"""
if not banned_keywords or len(file_ids) != len(file_names):
return file_ids
filtered_ids: List[str] = []
for fid, name in zip(file_ids, file_names):
name_lower: str = str(name).lower()
if any(kw.lower() in name_lower for kw in banned_keywords):
logger.info(
"[XunleiCleanup] Filtered ad file: '%s' (id=%s)", name, fid
)
continue
filtered_ids.append(fid)
return filtered_ids
def close(self) -> None:
"""关闭 HTTP 会话。"""
self.session.close()
def __enter__(self) -> "XunleiCleanup":
return self
def __exit__(self, *args: Any) -> None:
self.close()