mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 23:47:41 +08:00
refactor:减少Alipan及115的Api调用
This commit is contained in:
@@ -5,7 +5,7 @@ import secrets
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Dict, Optional, Tuple, Union
|
from typing import List, Optional, Tuple, Union
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
@@ -52,9 +52,6 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
# 基础url
|
# 基础url
|
||||||
base_url = "https://openapi.alipan.com"
|
base_url = "https://openapi.alipan.com"
|
||||||
|
|
||||||
# CID和路径缓存
|
|
||||||
_id_cache: Dict[str, Tuple[str, str]] = {}
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
@@ -279,61 +276,6 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
return ret_data.get(result_key)
|
return ret_data.get(result_key)
|
||||||
return ret_data
|
return ret_data
|
||||||
|
|
||||||
def _path_to_id(self, drive_id: str, path: str) -> Tuple[str, str]:
|
|
||||||
"""
|
|
||||||
路径转drive_id, file_id(带缓存机制)
|
|
||||||
"""
|
|
||||||
# 根目录
|
|
||||||
if path == "/":
|
|
||||||
return drive_id, "root"
|
|
||||||
if len(path) > 1 and path.endswith("/"):
|
|
||||||
path = path[:-1]
|
|
||||||
# 检查缓存
|
|
||||||
if path in self._id_cache:
|
|
||||||
return self._id_cache[path]
|
|
||||||
# 逐级查找缓存
|
|
||||||
file_id = "root"
|
|
||||||
file_path = "/"
|
|
||||||
for p in Path(path).parents:
|
|
||||||
if str(p) in self._id_cache:
|
|
||||||
file_path = str(p)
|
|
||||||
file_id = self._id_cache[file_path]
|
|
||||||
break
|
|
||||||
# 计算相对路径
|
|
||||||
rel_path = Path(path).relative_to(file_path)
|
|
||||||
for part in Path(rel_path).parts:
|
|
||||||
find_part = False
|
|
||||||
next_marker = None
|
|
||||||
while True:
|
|
||||||
resp = self._request_api(
|
|
||||||
"POST",
|
|
||||||
"/adrive/v1.0/openFile/list",
|
|
||||||
json={
|
|
||||||
"drive_id": drive_id,
|
|
||||||
"limit": 100,
|
|
||||||
"marker": next_marker,
|
|
||||||
"parent_file_id": file_id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if not resp:
|
|
||||||
break
|
|
||||||
for item in resp.get("items", []):
|
|
||||||
if item["name"] == part:
|
|
||||||
file_id = item["file_id"]
|
|
||||||
find_part = True
|
|
||||||
break
|
|
||||||
if find_part:
|
|
||||||
break
|
|
||||||
if len(resp.get("items")) < 100:
|
|
||||||
break
|
|
||||||
if not find_part:
|
|
||||||
raise FileNotFoundError(f"【阿里云盘】{path} 不存在")
|
|
||||||
if file_id == "root":
|
|
||||||
raise FileNotFoundError(f"【阿里云盘】{path} 不存在")
|
|
||||||
# 缓存路径
|
|
||||||
self._id_cache[path] = (drive_id, file_id)
|
|
||||||
return drive_id, file_id
|
|
||||||
|
|
||||||
def __get_fileitem(self, fileinfo: dict, parent: str = "/") -> schemas.FileItem:
|
def __get_fileitem(self, fileinfo: dict, parent: str = "/") -> schemas.FileItem:
|
||||||
"""
|
"""
|
||||||
获取文件信息
|
获取文件信息
|
||||||
@@ -427,9 +369,6 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
break
|
break
|
||||||
next_marker = resp.get("next_marker")
|
next_marker = resp.get("next_marker")
|
||||||
for item in resp.get("items", []):
|
for item in resp.get("items", []):
|
||||||
# 更新缓存
|
|
||||||
path = f"{fileitem.path}{item.get('name')}"
|
|
||||||
self._id_cache[path] = (drive_id, item.get("file_id"))
|
|
||||||
items.append(self.__get_fileitem(item, parent=fileitem.path))
|
items.append(self.__get_fileitem(item, parent=fileitem.path))
|
||||||
if len(resp.get("items")) < 100:
|
if len(resp.get("items")) < 100:
|
||||||
break
|
break
|
||||||
@@ -467,7 +406,6 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
return None
|
return None
|
||||||
# 缓存新目录
|
# 缓存新目录
|
||||||
new_path = Path(parent_item.path) / name
|
new_path = Path(parent_item.path) / name
|
||||||
self._id_cache[str(new_path)] = (resp.get("drive_id"), resp.get("file_id"))
|
|
||||||
return self._delay_get_item(new_path)
|
return self._delay_get_item(new_path)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -837,15 +775,9 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
if resp.get("code"):
|
if resp.get("code"):
|
||||||
logger.warn(f"【阿里云盘】重命名失败: {resp.get('message')}")
|
logger.warn(f"【阿里云盘】重命名失败: {resp.get('message')}")
|
||||||
return False
|
return False
|
||||||
if fileitem.path in self._id_cache:
|
|
||||||
del self._id_cache[fileitem.path]
|
|
||||||
for key in list(self._id_cache.keys()):
|
|
||||||
if key.startswith(fileitem.path):
|
|
||||||
del self._id_cache[key]
|
|
||||||
self._id_cache[str(Path(fileitem.path).parent / name)] = (resp.get("drive_id"), resp.get("file_id"))
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_item(self, path: Path) -> Optional[schemas.FileItem]:
|
def get_item(self, path: Path, drive_id: str = None) -> Optional[schemas.FileItem]:
|
||||||
"""
|
"""
|
||||||
获取指定路径的文件/目录项
|
获取指定路径的文件/目录项
|
||||||
"""
|
"""
|
||||||
@@ -854,7 +786,7 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
"POST",
|
"POST",
|
||||||
"/adrive/v1.0/openFile/get_by_path",
|
"/adrive/v1.0/openFile/get_by_path",
|
||||||
json={
|
json={
|
||||||
"drive_id": self._default_drive_id,
|
"drive_id": drive_id or self._default_drive_id,
|
||||||
"file_path": str(path)
|
"file_path": str(path)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -910,9 +842,15 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
|
|
||||||
def copy(self, fileitem: schemas.FileItem, path: Path, new_name: str) -> bool:
|
def copy(self, fileitem: schemas.FileItem, path: Path, new_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
企业级复制实现(支持目录递归复制)
|
复制文件到指定路径
|
||||||
|
:param fileitem: 要复制的文件项
|
||||||
|
:param path: 目标目录路径
|
||||||
|
:param new_name: 新文件名
|
||||||
"""
|
"""
|
||||||
dest_cid = self._path_to_id(fileitem.drive_id, str(path))
|
dest_fileitem = self.get_item(path, drive_id=fileitem.drive_id)
|
||||||
|
if not dest_fileitem or dest_fileitem.type != "dir":
|
||||||
|
logger.warn(f"【阿里云盘】目标路径 {path} 不存在或不是目录!")
|
||||||
|
return False
|
||||||
resp = self._request_api(
|
resp = self._request_api(
|
||||||
"POST",
|
"POST",
|
||||||
"/adrive/v1.0/openFile/copy",
|
"/adrive/v1.0/openFile/copy",
|
||||||
@@ -920,7 +858,7 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
"drive_id": fileitem.drive_id,
|
"drive_id": fileitem.drive_id,
|
||||||
"file_id": fileitem.fileid,
|
"file_id": fileitem.fileid,
|
||||||
"to_drive_id": fileitem.drive_id,
|
"to_drive_id": fileitem.drive_id,
|
||||||
"to_parent_file_id": dest_cid
|
"to_parent_file_id": dest_fileitem.fileid,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if not resp:
|
if not resp:
|
||||||
@@ -932,18 +870,20 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
new_path = Path(path) / fileitem.name
|
new_path = Path(path) / fileitem.name
|
||||||
new_file = self._delay_get_item(new_path)
|
new_file = self._delay_get_item(new_path)
|
||||||
self.rename(new_file, new_name)
|
self.rename(new_file, new_name)
|
||||||
# 更新缓存
|
|
||||||
del self._id_cache[fileitem.path]
|
|
||||||
rename_new_path = Path(path) / new_name
|
|
||||||
self._id_cache[str(rename_new_path)] = (resp.get("drive_id"), resp.get("file_id"))
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def move(self, fileitem: schemas.FileItem, path: Path, new_name: str) -> bool:
|
def move(self, fileitem: schemas.FileItem, path: Path, new_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
原子性移动操作实现
|
移动文件到指定路径
|
||||||
|
:param fileitem: 要移动的文件项
|
||||||
|
:param path: 目标目录路径
|
||||||
|
:param new_name: 新文件名
|
||||||
"""
|
"""
|
||||||
src_fid = fileitem.fileid
|
src_fid = fileitem.fileid
|
||||||
target_id = self._path_to_id(fileitem.drive_id, str(path))
|
target_fileitem = self.get_item(path, drive_id=fileitem.drive_id)
|
||||||
|
if not target_fileitem or target_fileitem.type != "dir":
|
||||||
|
logger.warn(f"【阿里云盘】目标路径 {path} 不存在或不是目录!")
|
||||||
|
return False
|
||||||
|
|
||||||
resp = self._request_api(
|
resp = self._request_api(
|
||||||
"POST",
|
"POST",
|
||||||
@@ -951,7 +891,7 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
json={
|
json={
|
||||||
"drive_id": fileitem.drive_id,
|
"drive_id": fileitem.drive_id,
|
||||||
"file_id": src_fid,
|
"file_id": src_fid,
|
||||||
"to_parent_file_id": target_id,
|
"to_parent_file_id": target_fileitem.fileid,
|
||||||
"new_name": new_name
|
"new_name": new_name
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -960,10 +900,6 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
if resp.get("code"):
|
if resp.get("code"):
|
||||||
logger.warn(f"【阿里云盘】移动文件失败: {resp.get('message')}")
|
logger.warn(f"【阿里云盘】移动文件失败: {resp.get('message')}")
|
||||||
return False
|
return False
|
||||||
# 更新缓存
|
|
||||||
del self._id_cache[fileitem.path]
|
|
||||||
rename_new_path = Path(path) / new_name
|
|
||||||
self._id_cache[str(rename_new_path)] = (resp.get("drive_id"), resp.get("file_id"))
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def link(self, fileitem: schemas.FileItem, target_file: Path) -> bool:
|
def link(self, fileitem: schemas.FileItem, target_file: Path) -> bool:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import secrets
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Dict, Optional, Tuple, Union
|
from typing import List, Optional, Tuple, Union
|
||||||
|
|
||||||
import oss2
|
import oss2
|
||||||
import requests
|
import requests
|
||||||
@@ -51,9 +51,6 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
# 基础url
|
# 基础url
|
||||||
base_url = "https://proapi.115.com"
|
base_url = "https://proapi.115.com"
|
||||||
|
|
||||||
# CID和路径缓存
|
|
||||||
_id_cache: Dict[str, str] = {}
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
@@ -238,58 +235,6 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
return ret_data.get(result_key)
|
return ret_data.get(result_key)
|
||||||
return ret_data
|
return ret_data
|
||||||
|
|
||||||
def _path_to_id(self, path: str) -> str:
|
|
||||||
"""
|
|
||||||
路径转FID(带缓存机制)
|
|
||||||
"""
|
|
||||||
# 根目录
|
|
||||||
if path == "/":
|
|
||||||
return '0'
|
|
||||||
if len(path) > 1 and path.endswith("/"):
|
|
||||||
path = path[:-1]
|
|
||||||
# 检查缓存
|
|
||||||
if path in self._id_cache:
|
|
||||||
return self._id_cache[path]
|
|
||||||
# 逐级查找缓存
|
|
||||||
current_id = 0
|
|
||||||
parent_path = "/"
|
|
||||||
for p in Path(path).parents:
|
|
||||||
if str(p) in self._id_cache:
|
|
||||||
parent_path = str(p)
|
|
||||||
current_id = self._id_cache[parent_path]
|
|
||||||
break
|
|
||||||
# 计算相对路径
|
|
||||||
rel_path = Path(path).relative_to(parent_path)
|
|
||||||
for part in Path(rel_path).parts:
|
|
||||||
offset = 0
|
|
||||||
find_part = False
|
|
||||||
while True:
|
|
||||||
resp = self._request_api(
|
|
||||||
"GET",
|
|
||||||
"/open/ufile/files",
|
|
||||||
"data",
|
|
||||||
params={"cid": current_id, "limit": 1000, "offset": offset, "cur": True, "show_dir": 1}
|
|
||||||
)
|
|
||||||
if not resp:
|
|
||||||
break
|
|
||||||
for item in resp:
|
|
||||||
if item["fn"] == part:
|
|
||||||
current_id = item["fid"]
|
|
||||||
find_part = True
|
|
||||||
break
|
|
||||||
if find_part:
|
|
||||||
break
|
|
||||||
if len(resp) < 1000:
|
|
||||||
break
|
|
||||||
offset += len(resp)
|
|
||||||
if not find_part:
|
|
||||||
raise FileNotFoundError(f"【115】{path} 不存在")
|
|
||||||
if not current_id:
|
|
||||||
raise FileNotFoundError(f"【115】{path} 不存在")
|
|
||||||
# 缓存路径
|
|
||||||
self._id_cache[path] = str(current_id)
|
|
||||||
return str(current_id)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _calc_sha1(filepath: Path, size: Optional[int] = None) -> str:
|
def _calc_sha1(filepath: Path, size: Optional[int] = None) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -335,7 +280,11 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
else:
|
else:
|
||||||
cid = fileitem.fileid
|
cid = fileitem.fileid
|
||||||
if not cid:
|
if not cid:
|
||||||
cid = self._path_to_id(fileitem.path)
|
_fileitem = self.get_item(Path(fileitem.path))
|
||||||
|
if not _fileitem:
|
||||||
|
logger.warn(f"【115】获取目录 {fileitem.path} 失败!")
|
||||||
|
return []
|
||||||
|
cid = _fileitem.fileid
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
offset = 0
|
offset = 0
|
||||||
@@ -354,8 +303,6 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
for item in resp:
|
for item in resp:
|
||||||
# 更新缓存
|
# 更新缓存
|
||||||
path = f"{fileitem.path}{item['fn']}"
|
path = f"{fileitem.path}{item['fn']}"
|
||||||
self._id_cache[path] = str(item["fid"])
|
|
||||||
|
|
||||||
file_path = path + ("/" if item["fc"] == "0" else "")
|
file_path = path + ("/" if item["fc"] == "0" else "")
|
||||||
items.append(schemas.FileItem(
|
items.append(schemas.FileItem(
|
||||||
storage=self.schema.value,
|
storage=self.schema.value,
|
||||||
@@ -398,8 +345,6 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
return self.get_item(new_path)
|
return self.get_item(new_path)
|
||||||
logger.warn(f"【115】创建目录失败: {resp.get('error')}")
|
logger.warn(f"【115】创建目录失败: {resp.get('error')}")
|
||||||
return None
|
return None
|
||||||
# 缓存新目录
|
|
||||||
self._id_cache[str(new_path)] = str(resp["data"]["file_id"])
|
|
||||||
return schemas.FileItem(
|
return schemas.FileItem(
|
||||||
storage=self.schema.value,
|
storage=self.schema.value,
|
||||||
fileid=str(resp["data"]["file_id"]),
|
fileid=str(resp["data"]["file_id"]),
|
||||||
@@ -716,13 +661,6 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
if not resp:
|
if not resp:
|
||||||
return False
|
return False
|
||||||
if resp["state"]:
|
if resp["state"]:
|
||||||
if fileitem.path in self._id_cache:
|
|
||||||
del self._id_cache[fileitem.path]
|
|
||||||
for key in list(self._id_cache.keys()):
|
|
||||||
if key.startswith(fileitem.path):
|
|
||||||
del self._id_cache[key]
|
|
||||||
new_path = Path(fileitem.path).parent / name
|
|
||||||
self._id_cache[str(new_path)] = fileitem.fileid
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -731,15 +669,12 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
获取指定路径的文件/目录项
|
获取指定路径的文件/目录项
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
file_id = self._path_to_id(str(path))
|
|
||||||
if not file_id:
|
|
||||||
return None
|
|
||||||
resp = self._request_api(
|
resp = self._request_api(
|
||||||
"GET",
|
"POST",
|
||||||
"/open/folder/get_info",
|
"/open/folder/get_info",
|
||||||
"data",
|
"data",
|
||||||
params={
|
data={
|
||||||
"file_id": int(file_id)
|
"path": str(path)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if not resp:
|
if not resp:
|
||||||
@@ -753,7 +688,7 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
basename=Path(resp["file_name"]).stem,
|
basename=Path(resp["file_name"]).stem,
|
||||||
extension=Path(resp["file_name"]).suffix[1:] if resp["file_category"] == "1" else None,
|
extension=Path(resp["file_name"]).suffix[1:] if resp["file_category"] == "1" else None,
|
||||||
pickcode=resp["pick_code"],
|
pickcode=resp["pick_code"],
|
||||||
size=StringUtils.num_filesize(resp['size']) if resp["file_category"] == "1" else None,
|
size=resp['size_byte'] if resp["file_category"] == "1" else None,
|
||||||
modify_time=resp["utime"]
|
modify_time=resp["utime"]
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -805,14 +740,17 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
企业级复制实现(支持目录递归复制)
|
企业级复制实现(支持目录递归复制)
|
||||||
"""
|
"""
|
||||||
src_fid = fileitem.fileid
|
src_fid = fileitem.fileid
|
||||||
dest_cid = self._path_to_id(str(path))
|
dest_fileitem = self.get_item(path)
|
||||||
|
if not dest_fileitem or dest_fileitem.type != "dir":
|
||||||
|
logger.warn(f"【115】目标路径 {path} 不是一个有效的目录!")
|
||||||
|
return False
|
||||||
|
|
||||||
resp = self._request_api(
|
resp = self._request_api(
|
||||||
"POST",
|
"POST",
|
||||||
"/open/ufile/copy",
|
"/open/ufile/copy",
|
||||||
data={
|
data={
|
||||||
"file_id": int(src_fid),
|
"file_id": int(src_fid),
|
||||||
"pid": int(dest_cid)
|
"pid": int(dest_fileitem.fileid),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if not resp:
|
if not resp:
|
||||||
@@ -821,10 +759,6 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
new_path = Path(path) / fileitem.name
|
new_path = Path(path) / fileitem.name
|
||||||
new_item = self._delay_get_item(new_path)
|
new_item = self._delay_get_item(new_path)
|
||||||
self.rename(new_item, new_name)
|
self.rename(new_item, new_name)
|
||||||
# 更新缓存
|
|
||||||
del self._id_cache[fileitem.path]
|
|
||||||
rename_new_path = Path(path) / new_name
|
|
||||||
self._id_cache[str(rename_new_path)] = new_item.fileid
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -833,14 +767,16 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
原子性移动操作实现
|
原子性移动操作实现
|
||||||
"""
|
"""
|
||||||
src_fid = fileitem.fileid
|
src_fid = fileitem.fileid
|
||||||
dest_cid = self._path_to_id(str(path))
|
dest_fileitem = self.get_item(path)
|
||||||
|
if not dest_fileitem or dest_fileitem.type != "dir":
|
||||||
|
logger.warn(f"【115】目标路径 {path} 不是一个有效的目录!")
|
||||||
|
return False
|
||||||
resp = self._request_api(
|
resp = self._request_api(
|
||||||
"POST",
|
"POST",
|
||||||
"/open/ufile/move",
|
"/open/ufile/move",
|
||||||
data={
|
data={
|
||||||
"file_ids": int(src_fid),
|
"file_ids": int(src_fid),
|
||||||
"to_cid": int(dest_cid)
|
"to_cid": int(dest_fileitem.fileid),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if not resp:
|
if not resp:
|
||||||
@@ -849,10 +785,6 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
new_path = Path(path) / fileitem.name
|
new_path = Path(path) / fileitem.name
|
||||||
new_file = self._delay_get_item(new_path)
|
new_file = self._delay_get_item(new_path)
|
||||||
self.rename(new_file, new_name)
|
self.rename(new_file, new_name)
|
||||||
# 更新缓存
|
|
||||||
del self._id_cache[fileitem.path]
|
|
||||||
rename_new_path = Path(path) / new_name
|
|
||||||
self._id_cache[str(rename_new_path)] = src_fid
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user