mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-25 02:00:13 +08:00
fix:优化下载
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import Optional, List, Tuple, Union
|
from typing import Optional, List, Tuple, Union
|
||||||
|
|
||||||
@@ -456,36 +457,53 @@ class MediaChain(ChainBase):
|
|||||||
"""
|
"""
|
||||||
if not _fileitem or not _content or not _path:
|
if not _fileitem or not _content or not _path:
|
||||||
return
|
return
|
||||||
# 保存文件到临时目录
|
# 使用tempfile创建临时文件,自动删除
|
||||||
tmp_dir = settings.TEMP_PATH / StringUtils.generate_random_str(10)
|
with NamedTemporaryFile(delete=True, suffix=_path.suffix) as tmp_file:
|
||||||
tmp_dir.mkdir(parents=True, exist_ok=True)
|
# 写入内容
|
||||||
tmp_file = tmp_dir / _path.name
|
if isinstance(_content, bytes):
|
||||||
tmp_file.write_bytes(_content)
|
tmp_file.write(_content)
|
||||||
# 获取文件的父目录
|
else:
|
||||||
try:
|
tmp_file.write(_content.encode('utf-8'))
|
||||||
item = storagechain.upload_file(fileitem=_fileitem, path=tmp_file, new_name=_path.name)
|
tmp_file.flush()
|
||||||
|
# 上传文件
|
||||||
|
item = storagechain.upload_file(fileitem=_fileitem, path=Path(tmp_file.name), new_name=_path.name)
|
||||||
if item:
|
if item:
|
||||||
logger.info(f"已保存文件:{item.path}")
|
logger.info(f"已保存文件:{item.path}")
|
||||||
else:
|
else:
|
||||||
logger.warn(f"文件保存失败:{_path}")
|
logger.warn(f"文件保存失败:{_path}")
|
||||||
finally:
|
|
||||||
if tmp_file.exists():
|
|
||||||
tmp_file.unlink()
|
|
||||||
|
|
||||||
def __download_image(_url: str) -> Optional[bytes]:
|
def __download_and_save_image(_fileitem: schemas.FileItem, _path: Path, _url: str):
|
||||||
"""
|
"""
|
||||||
下载图片并保存
|
流式下载图片并直接保存到文件(减少内存占用)
|
||||||
|
:param _fileitem: 关联的媒体文件项
|
||||||
|
:param _path: 图片文件路径
|
||||||
|
:param _url: 图片下载URL
|
||||||
"""
|
"""
|
||||||
|
if not _fileitem or not _url or not _path:
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
logger.info(f"正在下载图片:{_url} ...")
|
logger.info(f"正在下载图片:{_url} ...")
|
||||||
r = RequestUtils(proxies=settings.PROXY, ua=settings.NORMAL_USER_AGENT).get_res(url=_url)
|
request_utils = RequestUtils(proxies=settings.PROXY, ua=settings.NORMAL_USER_AGENT)
|
||||||
if r:
|
with request_utils.get_stream(url=_url) as r:
|
||||||
return r.content
|
if r and r.status_code == 200:
|
||||||
else:
|
# 使用tempfile创建临时文件,自动删除
|
||||||
logger.info(f"{_url} 图片下载失败,请检查网络连通性!")
|
with NamedTemporaryFile(delete=True, suffix=_path.suffix) as tmp_file:
|
||||||
|
# 流式写入文件
|
||||||
|
for chunk in r.iter_content(chunk_size=8192):
|
||||||
|
if chunk:
|
||||||
|
tmp_file.write(chunk)
|
||||||
|
tmp_file.flush()
|
||||||
|
# 上传文件
|
||||||
|
item = storagechain.upload_file(fileitem=_fileitem, path=Path(tmp_file.name),
|
||||||
|
new_name=_path.name)
|
||||||
|
if item:
|
||||||
|
logger.info(f"已保存图片:{item.path}")
|
||||||
|
else:
|
||||||
|
logger.warn(f"图片保存失败:{_path}")
|
||||||
|
else:
|
||||||
|
logger.info(f"{_url} 图片下载失败")
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logger.error(f"{_url} 图片下载失败:{str(err)}!")
|
logger.error(f"{_url} 图片下载失败:{str(err)}!")
|
||||||
return None
|
|
||||||
|
|
||||||
if not fileitem:
|
if not fileitem:
|
||||||
return
|
return
|
||||||
@@ -587,11 +605,8 @@ class MediaChain(ChainBase):
|
|||||||
image_path = filepath.with_name(image_name)
|
image_path = filepath.with_name(image_name)
|
||||||
if overwrite or not storagechain.get_file_item(storage=fileitem.storage,
|
if overwrite or not storagechain.get_file_item(storage=fileitem.storage,
|
||||||
path=image_path):
|
path=image_path):
|
||||||
# 下载图片
|
# 流式下载图片并直接保存
|
||||||
content = __download_image(image_url)
|
__download_and_save_image(_fileitem=fileitem, _path=image_path, _url=image_url)
|
||||||
# 写入图片到当前目录
|
|
||||||
if content:
|
|
||||||
__save_file(_fileitem=fileitem, _path=image_path, _content=content)
|
|
||||||
else:
|
else:
|
||||||
logger.info(f"已存在图片文件:{image_path}")
|
logger.info(f"已存在图片文件:{image_path}")
|
||||||
else:
|
else:
|
||||||
@@ -637,13 +652,10 @@ class MediaChain(ChainBase):
|
|||||||
for episode, image_url in image_dict.items():
|
for episode, image_url in image_dict.items():
|
||||||
image_path = filepath.with_suffix(Path(image_url).suffix)
|
image_path = filepath.with_suffix(Path(image_url).suffix)
|
||||||
if overwrite or not storagechain.get_file_item(storage=fileitem.storage, path=image_path):
|
if overwrite or not storagechain.get_file_item(storage=fileitem.storage, path=image_path):
|
||||||
# 下载图片
|
# 流式下载图片并直接保存
|
||||||
content = __download_image(image_url)
|
if not parent:
|
||||||
# 保存图片文件到当前目录
|
parent = storagechain.get_parent_item(fileitem)
|
||||||
if content:
|
__download_and_save_image(_fileitem=parent, _path=image_path, _url=image_url)
|
||||||
if not parent:
|
|
||||||
parent = storagechain.get_parent_item(fileitem)
|
|
||||||
__save_file(_fileitem=parent, _path=image_path, _content=content)
|
|
||||||
else:
|
else:
|
||||||
logger.info(f"已存在图片文件:{image_path}")
|
logger.info(f"已存在图片文件:{image_path}")
|
||||||
else:
|
else:
|
||||||
@@ -694,13 +706,10 @@ class MediaChain(ChainBase):
|
|||||||
image_path = filepath.with_name(image_name)
|
image_path = filepath.with_name(image_name)
|
||||||
if overwrite or not storagechain.get_file_item(storage=fileitem.storage,
|
if overwrite or not storagechain.get_file_item(storage=fileitem.storage,
|
||||||
path=image_path):
|
path=image_path):
|
||||||
# 下载图片
|
# 流式下载图片并直接保存
|
||||||
content = __download_image(image_url)
|
if not parent:
|
||||||
# 保存图片文件到剧集目录
|
parent = storagechain.get_parent_item(fileitem)
|
||||||
if content:
|
__download_and_save_image(_fileitem=parent, _path=image_path, _url=image_url)
|
||||||
if not parent:
|
|
||||||
parent = storagechain.get_parent_item(fileitem)
|
|
||||||
__save_file(_fileitem=parent, _path=image_path, _content=content)
|
|
||||||
else:
|
else:
|
||||||
logger.info(f"已存在图片文件:{image_path}")
|
logger.info(f"已存在图片文件:{image_path}")
|
||||||
else:
|
else:
|
||||||
@@ -730,13 +739,11 @@ class MediaChain(ChainBase):
|
|||||||
continue
|
continue
|
||||||
if overwrite or not storagechain.get_file_item(storage=fileitem.storage,
|
if overwrite or not storagechain.get_file_item(storage=fileitem.storage,
|
||||||
path=image_path):
|
path=image_path):
|
||||||
# 下载图片
|
# 流式下载图片并直接保存
|
||||||
content = __download_image(image_url)
|
if not parent:
|
||||||
# 保存图片文件到当前目录
|
parent = storagechain.get_parent_item(fileitem)
|
||||||
if content:
|
__download_and_save_image(_fileitem=parent, _path=image_path,
|
||||||
if not parent:
|
_url=image_url)
|
||||||
parent = storagechain.get_parent_item(fileitem)
|
|
||||||
__save_file(_fileitem=parent, _path=image_path, _content=content)
|
|
||||||
else:
|
else:
|
||||||
logger.info(f"已存在图片文件:{image_path}")
|
logger.info(f"已存在图片文件:{image_path}")
|
||||||
else:
|
else:
|
||||||
@@ -786,11 +793,8 @@ class MediaChain(ChainBase):
|
|||||||
image_path = filepath / image_name
|
image_path = filepath / image_name
|
||||||
if overwrite or not storagechain.get_file_item(storage=fileitem.storage,
|
if overwrite or not storagechain.get_file_item(storage=fileitem.storage,
|
||||||
path=image_path):
|
path=image_path):
|
||||||
# 下载图片
|
# 流式下载图片并直接保存
|
||||||
content = __download_image(image_url)
|
__download_and_save_image(_fileitem=fileitem, _path=image_path, _url=image_url)
|
||||||
# 保存图片文件到当前目录
|
|
||||||
if content:
|
|
||||||
__save_file(_fileitem=fileitem, _path=image_path, _content=content)
|
|
||||||
else:
|
else:
|
||||||
logger.info(f"已存在图片文件:{image_path}")
|
logger.info(f"已存在图片文件:{image_path}")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ def transfer_process(path: str) -> Callable[[int | float], None]:
|
|||||||
"""
|
"""
|
||||||
传输进度回调
|
传输进度回调
|
||||||
"""
|
"""
|
||||||
pbar = tqdm(total=100, desc="整理进度", unit="%")
|
pbar = tqdm(total=100, desc="进度", unit="%")
|
||||||
progress = ProgressHelper(HashUtils.md5(path))
|
progress = ProgressHelper(HashUtils.md5(path))
|
||||||
progress.start()
|
progress.start()
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from app.log import logger
|
|||||||
from app.modules.filemanager import StorageBase
|
from app.modules.filemanager import StorageBase
|
||||||
from app.modules.filemanager.storages import transfer_process
|
from app.modules.filemanager.storages import transfer_process
|
||||||
from app.schemas.types import StorageSchema
|
from app.schemas.types import StorageSchema
|
||||||
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.singleton import WeakSingleton
|
from app.utils.singleton import WeakSingleton
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
@@ -729,7 +730,25 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
|
|||||||
progress_callback = transfer_process(Path(fileitem.path).as_posix())
|
progress_callback = transfer_process(Path(fileitem.path).as_posix())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with requests.get(download_url, stream=True) as r:
|
# 构建请求头,包含必要的认证信息
|
||||||
|
headers = {
|
||||||
|
"User-Agent": settings.NORMAL_USER_AGENT,
|
||||||
|
"Referer": "https://www.aliyundrive.com/",
|
||||||
|
"Accept": "*/*",
|
||||||
|
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
||||||
|
"Accept-Encoding": "gzip, deflate, br",
|
||||||
|
"Connection": "keep-alive",
|
||||||
|
"Sec-Fetch-Dest": "empty",
|
||||||
|
"Sec-Fetch-Mode": "cors",
|
||||||
|
"Sec-Fetch-Site": "cross-site"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 如果有access_token,添加到请求头
|
||||||
|
if self.access_token:
|
||||||
|
headers["Authorization"] = f"Bearer {self.access_token}"
|
||||||
|
|
||||||
|
request_utils = RequestUtils(headers=headers)
|
||||||
|
with request_utils.get_stream(download_url, raise_exception=True) as r:
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
downloaded_size = 0
|
downloaded_size = 0
|
||||||
with open(local_path, "wb") as f:
|
with open(local_path, "wb") as f:
|
||||||
@@ -748,22 +767,13 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
|
|||||||
# 完成下载
|
# 完成下载
|
||||||
progress_callback(100)
|
progress_callback(100)
|
||||||
logger.info(f"【阿里云盘】下载完成: {fileitem.name}")
|
logger.info(f"【阿里云盘】下载完成: {fileitem.name}")
|
||||||
|
return local_path
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
logger.error(f"【阿里云盘】下载网络错误: {fileitem.name} - {str(e)}")
|
|
||||||
# 删除可能部分下载的文件
|
|
||||||
if local_path.exists():
|
|
||||||
local_path.unlink()
|
|
||||||
return None
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"【阿里云盘】下载失败: {fileitem.name} - {str(e)}")
|
logger.error(f"【阿里云盘】下载失败: {fileitem.name} - {str(e)}")
|
||||||
# 删除可能部分下载的文件
|
|
||||||
if local_path.exists():
|
if local_path.exists():
|
||||||
local_path.unlink()
|
local_path.unlink()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return local_path
|
|
||||||
|
|
||||||
def check(self) -> bool:
|
def check(self) -> bool:
|
||||||
return self.access_token is not None
|
return self.access_token is not None
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ from datetime import datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.core.cache import cached
|
from app.core.cache import cached
|
||||||
from app.core.config import settings, global_vars
|
from app.core.config import settings, global_vars
|
||||||
@@ -569,18 +567,22 @@ class Alist(StorageBase, metaclass=WeakSingleton):
|
|||||||
else:
|
else:
|
||||||
local_path = path / fileitem.name
|
local_path = path / fileitem.name
|
||||||
|
|
||||||
with requests.get(download_url, headers=self.__get_header_with_token(), stream=True) as r:
|
request_utils = RequestUtils(headers=self.__get_header_with_token())
|
||||||
r.raise_for_status()
|
try:
|
||||||
with open(local_path, "wb") as f:
|
with request_utils.get_stream(download_url, raise_exception=True) as r:
|
||||||
for chunk in r.iter_content(chunk_size=8192):
|
r.raise_for_status()
|
||||||
if global_vars.is_transfer_stopped(fileitem.path):
|
with open(local_path, "wb") as f:
|
||||||
logger.info(f"【OpenList】{fileitem.path} 下载已取消!")
|
for chunk in r.iter_content(chunk_size=8192):
|
||||||
return None
|
if global_vars.is_transfer_stopped(fileitem.path):
|
||||||
f.write(chunk)
|
logger.info(f"【OpenList】{fileitem.path} 下载已取消!")
|
||||||
|
return None
|
||||||
|
f.write(chunk)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"【OpenList】下载文件 {fileitem.path} 失败:{e}")
|
||||||
|
if local_path.exists():
|
||||||
|
return local_path
|
||||||
|
|
||||||
if local_path.exists():
|
return local_path
|
||||||
return local_path
|
|
||||||
return None
|
|
||||||
|
|
||||||
def upload(
|
def upload(
|
||||||
self, fileitem: schemas.FileItem, path: Path, new_name: Optional[str] = None, task: bool = False
|
self, fileitem: schemas.FileItem, path: Path, new_name: Optional[str] = None, task: bool = False
|
||||||
|
|||||||
Reference in New Issue
Block a user