fix rclone

This commit is contained in:
jxxghp
2024-07-02 20:32:32 +08:00
parent b37e50480a
commit 0da2bd6468
3 changed files with 218 additions and 29 deletions
+13 -12
View File
@@ -56,31 +56,32 @@ class FileManagerModule(_ModuleBase):
if not dirs: if not dirs:
return False, "未设置任何目录" return False, "未设置任何目录"
for d in dirs: for d in dirs:
# 下载目录
download_path = d.download_path download_path = d.download_path
if not download_path: if not download_path:
return False, f"{d.name} 的下载目录未设置" return False, f"{d.name} 的下载目录未设置"
if d.storage == "local" and not Path(download_path).exists(): if d.storage == "local" and not Path(download_path).exists():
return False, f"{d.name} 的下载目录 {download_path} 不存在" return False, f"{d.name} 的下载目录 {download_path} 不存在"
# 媒体库目录
library_path = d.library_path library_path = d.library_path
if not library_path: if not library_path:
return False, f"{d.name} 的媒体库目录未设置" return False, f"{d.name} 的媒体库目录未设置"
if d.library_storage == "local" and not Path(library_path).exists(): if d.library_storage == "local" and not Path(library_path).exists():
return False, f"{d.name} 的媒体库目录 {library_path} 不存在" return False, f"{d.name} 的媒体库目录 {library_path} 不存在"
# 检查软硬链接 # 硬链接
if d.transfer_type in ["link", "softlink"] \
and (d.storage != "local" or d.library_storage != "local"):
return False, f"{d.name} 不是本地存储,不支持软硬链接"
# 检查硬链接
if d.transfer_type == "link" \ if d.transfer_type == "link" \
and d.storage == "local" \
and d.library_storage == "local" \
and not SystemUtils.is_same_disk(Path(download_path), Path(library_path)): and not SystemUtils.is_same_disk(Path(download_path), Path(library_path)):
return False, f"{d.name} 的下载目录 {download_path} 与媒体库目录 {library_path} 不在同一磁盘,无法硬链接" return False, f"{d.name} 的下载目录 {download_path} 与媒体库目录 {library_path} 不在同一磁盘,无法硬链接"
# 检查网盘 # 存储
if d.storage != "local": storage_oper = self.__get_storage_oper(d.storage)
storage_oper = self.__get_storage_oper(d.storage) if not storage_oper:
if not storage_oper: return False, f"{d.name} 的存储类型 {d.storage} 不支持"
return False, f"{d.name} 的存储类型 {d.storage} 不支持" if not storage_oper.check():
if not storage_oper.check(): return False, f"{d.name} 的存储测试不通过"
return False, f"{d.name} 的存储测试不通过" if d.transfer_type and d.transfer_type not in storage_oper.support_transtype():
return False, f"{d.name} 的存储不支持 {d.transfer_type} 整理方式"
return True, "" return True, ""
+1 -1
View File
@@ -83,7 +83,7 @@ class StorageBase(metaclass=ABCMeta):
pass pass
@abstractmethod @abstractmethod
def download(self, fileitm: schemas.FileItem, path: Path): def download(self, fileitm: schemas.FileItem, path: Path) -> bool:
""" """
下载文件,保存到本地 下载文件,保存到本地
""" """
+204 -16
View File
@@ -1,3 +1,5 @@
import copy
import json
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from typing import Optional, List from typing import Optional, List
@@ -6,6 +8,7 @@ from app import schemas
from app.log import logger from app.log import logger
from app.modules.filemanager.storage import StorageBase from app.modules.filemanager.storage import StorageBase
from app.schemas.types import StorageSchema from app.schemas.types import StorageSchema
from app.utils.string import StringUtils
from app.utils.system import SystemUtils from app.utils.system import SystemUtils
@@ -16,6 +19,7 @@ class Rclone(StorageBase):
# 存储类型 # 存储类型
schema = StorageSchema.Rclone schema = StorageSchema.Rclone
# 支持的整理方式 # 支持的整理方式
transtype = { transtype = {
"move": "移动", "move": "移动",
@@ -32,37 +36,221 @@ class Rclone(StorageBase):
else: else:
return None return None
def __get_fileitem(self, path: Path):
"""
获取文件项
"""
return schemas.FileItem(
storage=self.schema.value,
type="file",
path=str(path).replace("\\", "/"),
name=path.name,
basename=path.stem,
extension=path.suffix[1:],
size=path.stat().st_size,
modify_time=path.stat().st_mtime,
)
def __get_rcloneitem(self, item: dict):
"""
获取rclone文件项
"""
return schemas.FileItem(
storage=self.schema.value,
type="dir" if item.get("IsDir") else "file",
path=item.get("Path"),
name=item.get("Name"),
basename=Path(item.get("Name")).stem,
extension=Path(item.get("Name")).suffix[1:],
size=item.get("Size"),
modify_time=StringUtils.str_to_timestamp(item.get("ModTime"))
)
def check(self) -> bool: def check(self) -> bool:
pass """
检查存储是否可用
"""
try:
retcode = subprocess.run(
['rclone', 'lsf', 'MP:'],
startupinfo=self.__get_hidden_shell()
).returncode
if retcode == 0:
return True
except Exception as err:
logger.error(f"rclone存储检查失败:{err}")
return False
def list(self, fileitm: schemas.FileItem) -> Optional[List[schemas.FileItem]]: def list(self, fileitm: schemas.FileItem) -> Optional[List[schemas.FileItem]]:
pass """
浏览文件
"""
try:
ret = subprocess.run(
[
'rclone', 'lsjson',
f'MP:{fileitm.path}'
],
capture_output=True,
startupinfo=self.__get_hidden_shell()
)
if ret.returncode == 0:
items = json.loads(ret.stdout)
return [self.__get_rcloneitem(item) for item in items]
except Exception as err:
logger.error(f"浏览文件失败:{err}")
return None
def create_folder(self, fileitm: schemas.FileItem, name: str) -> Optional[schemas.FileItem]: def create_folder(self, fileitm: schemas.FileItem, name: str) -> Optional[schemas.FileItem]:
pass """
创建目录
"""
try:
retcode = subprocess.run(
[
'rclone', 'mkdir',
f'MP:{fileitm.path}/{name}'
],
startupinfo=self.__get_hidden_shell()
).returncode
if retcode == 0:
ret_fileitem = copy.deepcopy(fileitm)
ret_fileitem.path = f"{fileitm.path}/{name}/"
ret_fileitem.name = name
return ret_fileitem
except Exception as err:
logger.error(f"创建目录失败:{err}")
return None
def get_folder(self, path: Path) -> Optional[schemas.FileItem]: def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
""" """
获取目录 根据文件路程获取目录,不存在则创建
""" """
pass
def __find_dir_name(_fileitem: schemas.FileItem, _name: str) -> Optional[schemas.FileItem]:
"""
查找下级目录中匹配名称的目录
"""
sub_files = self.list(_fileitem)
for sub_file in sub_files:
if sub_file.type != "dir":
continue
if sub_file.name == _name:
return sub_file
return None
# 逐级查找和创建目录
fileitem = schemas.FileItem(path="/")
for part in path.parts:
if part == "/":
continue
dir_file = __find_dir_name(fileitem, part)
if dir_file:
return dir_file
else:
dir_file = self.create_folder(dir_file, part)
if not dir_file:
logger.warn(f"rclone创建目录 {fileitem.path}{part} 失败!")
return None
fileitem = dir_file
return None
def delete(self, fileitm: schemas.FileItem) -> bool: def delete(self, fileitm: schemas.FileItem) -> bool:
pass """
删除文件
"""
try:
retcode = subprocess.run(
[
'rclone', 'deletefile',
f'MP:{fileitm.path}'
],
startupinfo=self.__get_hidden_shell()
).returncode
if retcode == 0:
return True
except Exception as err:
logger.error(f"删除文件失败:{err}")
return False
def rename(self, fileitm: schemas.FileItem, name: str) -> bool: def rename(self, fileitm: schemas.FileItem, name: str) -> bool:
pass """
重命名文件
"""
try:
retcode = subprocess.run(
[
'rclone', 'moveto',
f'MP:{fileitm.path}',
f'MP:{Path(fileitm.path).parent}/{name}'
],
startupinfo=self.__get_hidden_shell()
).returncode
if retcode == 0:
return True
except Exception as err:
logger.error(f"重命名文件失败:{err}")
return False
def download(self, fileitm: schemas.FileItem, path: Path): def download(self, fileitm: schemas.FileItem, path: Path) -> bool:
pass """
下载文件
"""
try:
retcode = subprocess.run(
[
'rclone', 'copyto',
f'MP:{fileitm.path}',
f'{path}'
],
startupinfo=self.__get_hidden_shell()
).returncode
if retcode == 0:
return True
except Exception as err:
logger.error(f"复制文件失败:{err}")
return False
def upload(self, fileitm: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]: def upload(self, fileitm: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
pass """
上传文件
"""
try:
retcode = subprocess.run(
[
'rclone', 'copyto',
fileitm.path,
f'MP:{path}'
],
startupinfo=self.__get_hidden_shell()
).returncode
if retcode == 0:
return self.__get_fileitem(path)
except Exception as err:
logger.error(f"上传文件失败:{err}")
return None
def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]: def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]:
pass """
获取文件详情
"""
try:
ret = subprocess.run(
[
'rclone', 'lsjson',
f'MP:{fileitm.path}'
],
capture_output=True,
startupinfo=self.__get_hidden_shell()
)
if ret.returncode == 0:
items = json.loads(ret.stdout)
return self.__get_rcloneitem(items[0])
except Exception as err:
logger.error(f"获取文件详情失败:{err}")
return None
def move(self, fileitm: schemas.FileItem, target_file: schemas.FileItem) -> bool: def move(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
""" """
移动文件,target_file格式:rclone:path 移动文件,target_file格式:rclone:path
""" """
@@ -70,8 +258,8 @@ class Rclone(StorageBase):
retcode = subprocess.run( retcode = subprocess.run(
[ [
'rclone', 'moveto', 'rclone', 'moveto',
fileitm.path, f'MP:{fileitm.path}',
f'{target_file}' f'MP:{target_file}'
], ],
startupinfo=self.__get_hidden_shell() startupinfo=self.__get_hidden_shell()
).returncode ).returncode
@@ -89,8 +277,8 @@ class Rclone(StorageBase):
retcode = subprocess.run( retcode = subprocess.run(
[ [
'rclone', 'copyto', 'rclone', 'copyto',
fileitm.path, f'MP:{fileitm.path}',
f'{target_file}' f'MP:{target_file}'
], ],
startupinfo=self.__get_hidden_shell() startupinfo=self.__get_hidden_shell()
).returncode ).returncode