mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-04-05 11:47:50 +08:00
fix storage
This commit is contained in:
@@ -9,7 +9,6 @@ from app.chain.transfer import TransferChain
|
||||
from app.core.config import settings
|
||||
from app.core.metainfo import MetaInfoPath
|
||||
from app.core.security import verify_token, verify_uri_token
|
||||
from app.modules.filetransfer.storage.alipan import AliyunHelper
|
||||
from app.helper.progress import ProgressHelper
|
||||
from app.schemas.types import ProgressKey
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ from app.core.config import settings
|
||||
from app.core.metainfo import MetaInfoPath
|
||||
from app.core.security import verify_token, verify_uri_token
|
||||
from app.helper.progress import ProgressHelper
|
||||
from app.modules.filetransfer.storage.u115 import U115Helper
|
||||
from app.schemas.types import ProgressKey
|
||||
from app.utils.http import RequestUtils
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ class FileTransferModule(_ModuleBase):
|
||||
pass
|
||||
|
||||
def __transfer_command(self, fileitem: FileItem, target_storage: str,
|
||||
target_file: Path, transfer_type: str) -> int:
|
||||
target_file: Path, transfer_type: str) -> bool:
|
||||
"""
|
||||
使用系统命令处理单个文件
|
||||
:param fileitem: 源文件
|
||||
@@ -192,29 +192,60 @@ class FileTransferModule(_ModuleBase):
|
||||
|
||||
if fileitem.storage != "local" and target_storage != "local":
|
||||
logger.error(f"不支持 {fileitem.storage} 到 {target_storage} 的文件整理")
|
||||
return 1
|
||||
retcode = 0
|
||||
return False
|
||||
# 源操作对象
|
||||
source_oper = self.__get_storage_oper(fileitem.storage)
|
||||
# 目的操作对象
|
||||
target_oper = self.__get_storage_oper(target_storage)
|
||||
|
||||
with lock:
|
||||
if fileitem.storage == "local" and target_storage == "local":
|
||||
# 本地到本地
|
||||
if transfer_type == "copy":
|
||||
retcode = source_oper.copy(fileitem, target_file)
|
||||
return source_oper.copy(fileitem, target_file)
|
||||
elif transfer_type == "move":
|
||||
retcode = source_oper.move(fileitem, target_file)
|
||||
return source_oper.move(fileitem, target_file)
|
||||
elif transfer_type == "link":
|
||||
retcode = source_oper.link(fileitem, target_file)
|
||||
return source_oper.link(fileitem, target_file)
|
||||
elif transfer_type == "softlink":
|
||||
retcode = source_oper.softlink(fileitem, target_file)
|
||||
# TODO 本地到网盘
|
||||
|
||||
# TODO 网盘到本地
|
||||
|
||||
return retcode
|
||||
return source_oper.softlink(fileitem, target_file)
|
||||
elif fileitem.storage == "local" and target_storage != "local":
|
||||
# 本地到网盘
|
||||
if transfer_type == "copy":
|
||||
# 复制
|
||||
filepath = Path(fileitem.path)
|
||||
if not filepath.exists():
|
||||
logger.error(f"文件 {filepath} 不存在")
|
||||
return False
|
||||
# TODO 根据目的路径创建文件夹
|
||||
target_fileitem = target_oper.get_folder(target_file.parent)
|
||||
if target_fileitem:
|
||||
# 上传文件
|
||||
return target_oper.upload(target_fileitem, filepath)
|
||||
elif transfer_type == "move":
|
||||
# 移动
|
||||
filepath = Path(fileitem.path)
|
||||
if not filepath.exists():
|
||||
logger.error(f"文件 {filepath} 不存在")
|
||||
return False
|
||||
# TODO 根据目的路径获取文件夹
|
||||
target_fileitem = target_oper.get_folder(target_file.parent)
|
||||
if target_fileitem:
|
||||
# 上传文件
|
||||
result = target_oper.upload(target_fileitem, filepath)
|
||||
if result:
|
||||
# 删除源文件
|
||||
return source_oper.delete(fileitem)
|
||||
elif fileitem.storage != "local" and target_storage == "local":
|
||||
# 网盘到本地
|
||||
if transfer_type == "copy":
|
||||
# 下载
|
||||
return target_oper.download(fileitem, target_file)
|
||||
elif transfer_type == "move":
|
||||
# 下载
|
||||
if target_oper.download(fileitem, target_file):
|
||||
# 删除源文件
|
||||
return source_oper.delete(fileitem)
|
||||
return False
|
||||
|
||||
def __transfer_other_files(self, fileitem: FileItem, target_storage: str, target_file: Path,
|
||||
transfer_type: str) -> int:
|
||||
|
||||
@@ -45,6 +45,13 @@ class StorageBase(metaclass=ABCMeta):
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
|
||||
"""
|
||||
获取目录
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def delete(self, fileitm: schemas.FileItem) -> bool:
|
||||
"""
|
||||
@@ -60,9 +67,9 @@ class StorageBase(metaclass=ABCMeta):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def download(self, fileitm: schemas.FileItem) -> Any:
|
||||
def download(self, fileitm: schemas.FileItem, path: Path):
|
||||
"""
|
||||
下载链接
|
||||
下载文件,保存到本地
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@@ -452,6 +452,12 @@ class AliPan(StorageBase):
|
||||
self.__handle_error(res, "创建目录")
|
||||
return None
|
||||
|
||||
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
|
||||
"""
|
||||
TODO 获取目录,不存在则创建
|
||||
"""
|
||||
pass
|
||||
|
||||
def delete(self, fileitem: schemas.FileItem) -> bool:
|
||||
"""
|
||||
删除文件
|
||||
@@ -520,27 +526,35 @@ class AliPan(StorageBase):
|
||||
self.__handle_error(res, "重命名文件")
|
||||
return False
|
||||
|
||||
def download(self, fileitem: schemas.FileItem) -> Optional[str]:
|
||||
def download(self, fileitem: schemas.FileItem, path: Path) -> bool:
|
||||
"""
|
||||
获取下载链接
|
||||
下载文件,保存到本地
|
||||
"""
|
||||
params = self.__access_params
|
||||
if not params:
|
||||
return None
|
||||
return False
|
||||
headers = self.__get_headers(params)
|
||||
res = RequestUtils(headers=headers, timeout=10).post_res(self.download_url, json={
|
||||
"drive_id": fileitem.drive_id,
|
||||
"file_id": fileitem.fileid
|
||||
})
|
||||
if res:
|
||||
return res.json().get("url")
|
||||
download_url = res.json().get("url")
|
||||
if not download_url:
|
||||
return False
|
||||
res = RequestUtils().get_res(download_url)
|
||||
if res:
|
||||
with path.open("wb") as f:
|
||||
f.write(res.content)
|
||||
return True
|
||||
else:
|
||||
self.__handle_error(res, "获取下载链接")
|
||||
return None
|
||||
return False
|
||||
|
||||
def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
|
||||
"""
|
||||
上传文件,并标记完成
|
||||
TODO 上传文件分片、秒传
|
||||
"""
|
||||
params = self.__access_params
|
||||
if not params:
|
||||
@@ -634,10 +648,19 @@ class AliPan(StorageBase):
|
||||
return False
|
||||
|
||||
def copy(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
|
||||
"""
|
||||
复制文件
|
||||
"""
|
||||
pass
|
||||
|
||||
def link(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
|
||||
"""
|
||||
硬链接文件
|
||||
"""
|
||||
pass
|
||||
|
||||
def softlink(self, fileitm: schemas.FileItem, target_file: schemas.FileItem) -> bool:
|
||||
"""
|
||||
软链接文件
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -2,8 +2,6 @@ import shutil
|
||||
from pathlib import Path
|
||||
from typing import Optional, List
|
||||
|
||||
from starlette.responses import FileResponse, Response
|
||||
|
||||
from app import schemas
|
||||
from app.log import logger
|
||||
from app.modules.filetransfer.storage import StorageBase
|
||||
@@ -117,6 +115,12 @@ class LocalStorage(StorageBase):
|
||||
modify_time=path_obj.stat().st_mtime,
|
||||
)
|
||||
|
||||
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
|
||||
"""
|
||||
获取目录
|
||||
"""
|
||||
pass
|
||||
|
||||
def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]:
|
||||
"""
|
||||
获取文件详情
|
||||
@@ -156,45 +160,17 @@ class LocalStorage(StorageBase):
|
||||
return False
|
||||
path_obj.rename(path_obj.parent / name)
|
||||
|
||||
def download(self, fileitem: schemas.FileItem) -> Optional[Response]:
|
||||
def download(self, fileitem: schemas.FileItem, path: Path) -> bool:
|
||||
"""
|
||||
下载文件
|
||||
"""
|
||||
if not fileitem.path:
|
||||
return None
|
||||
path_obj = Path(fileitem.path)
|
||||
if not path_obj.exists():
|
||||
return None
|
||||
if path_obj.is_file():
|
||||
# 做为文件流式下载
|
||||
return FileResponse(path_obj)
|
||||
else:
|
||||
# 做为压缩包下载
|
||||
shutil.make_archive(base_name=path_obj.stem, format="zip", root_dir=path_obj)
|
||||
reponse = Response(content=path_obj.read_bytes(), media_type="application/zip")
|
||||
# 删除压缩包
|
||||
Path(f"{path_obj.stem}.zip").unlink()
|
||||
return reponse
|
||||
return False
|
||||
|
||||
def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
|
||||
"""
|
||||
上传文件
|
||||
"""
|
||||
if not fileitem.path:
|
||||
return None
|
||||
path_obj = Path(fileitem.path)
|
||||
if not path_obj.exists():
|
||||
return None
|
||||
shutil.copy(path, path_obj / path.name)
|
||||
return schemas.FileItem(
|
||||
type="file",
|
||||
path=str(path_obj / path.name).replace("\\", "/"),
|
||||
name=path.name,
|
||||
basename=path.stem,
|
||||
extension=path.suffix[1:],
|
||||
size=path.stat().st_size,
|
||||
modify_time=path.stat().st_mtime,
|
||||
)
|
||||
return None
|
||||
|
||||
def copy(self, fileitem: schemas.FileItem, target_file: Path) -> bool:
|
||||
"""
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Optional, Any, List
|
||||
from typing import Optional, List
|
||||
|
||||
from app import schemas
|
||||
from app.log import logger
|
||||
from app.modules.filetransfer.storage import StorageBase
|
||||
from app.schemas.types import StorageSchema
|
||||
from app.utils.system import SystemUtils
|
||||
|
||||
|
||||
class Rclone(StorageBase):
|
||||
@@ -19,6 +22,16 @@ class Rclone(StorageBase):
|
||||
"copy": "复制"
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def __get_hidden_shell():
|
||||
if SystemUtils.is_windows():
|
||||
st = subprocess.STARTUPINFO()
|
||||
st.dwFlags = subprocess.STARTF_USESHOWWINDOW
|
||||
st.wShowWindow = subprocess.SW_HIDE
|
||||
return st
|
||||
else:
|
||||
return None
|
||||
|
||||
def check(self) -> bool:
|
||||
pass
|
||||
|
||||
@@ -28,13 +41,19 @@ class Rclone(StorageBase):
|
||||
def create_folder(self, fileitm: schemas.FileItem, name: str) -> Optional[schemas.FileItem]:
|
||||
pass
|
||||
|
||||
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
|
||||
"""
|
||||
获取目录
|
||||
"""
|
||||
pass
|
||||
|
||||
def delete(self, fileitm: schemas.FileItem) -> bool:
|
||||
pass
|
||||
|
||||
def rename(self, fileitm: schemas.FileItem, name: str) -> bool:
|
||||
pass
|
||||
|
||||
def download(self, fileitm: schemas.FileItem) -> Any:
|
||||
def download(self, fileitm: schemas.FileItem, path: Path) -> bool:
|
||||
pass
|
||||
|
||||
def upload(self, fileitm: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
|
||||
@@ -43,11 +62,43 @@ class Rclone(StorageBase):
|
||||
def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]:
|
||||
pass
|
||||
|
||||
def move(self, fileitm: schemas.FileItem, target_dir: schemas.FileItem) -> bool:
|
||||
pass
|
||||
def move(self, fileitm: schemas.FileItem, target_file: schemas.FileItem) -> bool:
|
||||
"""
|
||||
移动文件
|
||||
"""
|
||||
try:
|
||||
retcode = subprocess.run(
|
||||
[
|
||||
'rclone', 'moveto',
|
||||
fileitm.path,
|
||||
f'MP:{target_file}'
|
||||
],
|
||||
startupinfo=self.__get_hidden_shell()
|
||||
).returncode
|
||||
if retcode == 0:
|
||||
return True
|
||||
except Exception as err:
|
||||
logger.error(f"移动文件失败:{err}")
|
||||
return False
|
||||
|
||||
def copy(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
|
||||
pass
|
||||
"""
|
||||
复制文件
|
||||
"""
|
||||
try:
|
||||
retcode = subprocess.run(
|
||||
[
|
||||
'rclone', 'copyto',
|
||||
fileitm.path,
|
||||
f'MP:{target_file}'
|
||||
],
|
||||
startupinfo=self.__get_hidden_shell()
|
||||
).returncode
|
||||
if retcode == 0:
|
||||
return True
|
||||
except Exception as err:
|
||||
logger.error(f"复制文件失败:{err}")
|
||||
return False
|
||||
|
||||
def link(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
|
||||
pass
|
||||
|
||||
@@ -5,13 +5,14 @@ from typing import Optional, Tuple, List
|
||||
import oss2
|
||||
import py115
|
||||
from py115 import Cloud
|
||||
from py115.types import LoginTarget, QrcodeSession, QrcodeStatus, Credential, DownloadTicket
|
||||
from py115.types import LoginTarget, QrcodeSession, QrcodeStatus, Credential
|
||||
|
||||
from app import schemas
|
||||
from app.db.systemconfig_oper import SystemConfigOper
|
||||
from app.log import logger
|
||||
from app.modules.filetransfer.storage import StorageBase
|
||||
from app.schemas.types import SystemConfigKey, StorageSchema
|
||||
from app.utils.http import RequestUtils
|
||||
from app.utils.singleton import Singleton
|
||||
|
||||
|
||||
@@ -196,6 +197,12 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
||||
logger.error(f"创建115目录失败:{str(e)}")
|
||||
return None
|
||||
|
||||
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
|
||||
"""
|
||||
TODO 获取目录,不存在则创建
|
||||
"""
|
||||
pass
|
||||
|
||||
def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]:
|
||||
"""
|
||||
获取文件详情
|
||||
@@ -228,17 +235,23 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
||||
logger.error(f"重命名115文件失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def download(self, fileitem: schemas.FileItem) -> Optional[DownloadTicket]:
|
||||
def download(self, fileitem: schemas.FileItem, path: Path) -> bool:
|
||||
"""
|
||||
获取下载链接
|
||||
"""
|
||||
if not self.__init_cloud():
|
||||
return None
|
||||
return False
|
||||
try:
|
||||
return self.cloud.storage().request_download(fileitem.pickcode)
|
||||
ticket = self.cloud.storage().request_download(fileitem.pickcode)
|
||||
if ticket:
|
||||
res = RequestUtils(headers=ticket.headers).get_res(ticket.url)
|
||||
if res:
|
||||
with open(path, "wb") as f:
|
||||
f.write(res.content)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"115下载失败:{str(e)}")
|
||||
return None
|
||||
return False
|
||||
|
||||
def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
|
||||
"""
|
||||
|
||||
@@ -3,7 +3,6 @@ import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List, Union, Tuple
|
||||
@@ -144,54 +143,6 @@ class SystemUtils:
|
||||
print(str(err))
|
||||
return -1, str(err)
|
||||
|
||||
@staticmethod
|
||||
def rclone_move(src: Path, dest: Path):
|
||||
"""
|
||||
Rclone移动
|
||||
"""
|
||||
try:
|
||||
retcode = subprocess.run(
|
||||
[
|
||||
'rclone', 'moveto',
|
||||
str(src),
|
||||
f'MP:{dest}'
|
||||
],
|
||||
startupinfo=SystemUtils.__get_hidden_shell()
|
||||
).returncode
|
||||
return retcode, ""
|
||||
except Exception as err:
|
||||
print(str(err))
|
||||
return -1, str(err)
|
||||
|
||||
@staticmethod
|
||||
def rclone_copy(src: Path, dest: Path):
|
||||
"""
|
||||
Rclone复制
|
||||
"""
|
||||
try:
|
||||
retcode = subprocess.run(
|
||||
[
|
||||
'rclone', 'copyto',
|
||||
str(src),
|
||||
f'MP:{dest}'
|
||||
],
|
||||
startupinfo=SystemUtils.__get_hidden_shell()
|
||||
).returncode
|
||||
return retcode, ""
|
||||
except Exception as err:
|
||||
print(str(err))
|
||||
return -1, str(err)
|
||||
|
||||
@staticmethod
|
||||
def __get_hidden_shell():
|
||||
if SystemUtils.is_windows():
|
||||
st = subprocess.STARTUPINFO()
|
||||
st.dwFlags = subprocess.STARTF_USESHOWWINDOW
|
||||
st.wShowWindow = subprocess.SW_HIDE
|
||||
return st
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def list_files(directory: Path, extensions: list, min_filesize: int = 0) -> List[Path]:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user