fix lot of thing

This commit is contained in:
2025-11-01 20:14:35 +08:00
parent 39dfb62cbf
commit f006729311
16 changed files with 23303 additions and 3996 deletions

View File

@@ -371,14 +371,16 @@ class MiaoStarsBasicApi:
if path == "/" or path == "":
uri = "cloudreve://my/"
else:
# 清理路径确保不包含cloudreve://my前缀
normalized_path = path.strip("/").replace("\\", "/").replace("cloudreve://my/", "")
# 使用quote_plus正确编码路径确保特殊字符被正确处理
# 首先规范化路径分隔符
normalized_path = path.strip("/").replace("\\", "/")
# 对路径部分进行URL编码
encoded_path = quote_plus(normalized_path)
# 由于quote_plus会将斜杠也编码我们需要恢复它们
encoded_path = encoded_path.replace("%2F", "/")
uri = f"cloudreve://my/{encoded_path}"
# 确保路径格式正确,移除重复的前缀
uri = uri.replace("cloudreve://my/cloudreve://my", "cloudreve://my")
# 添加必需的分页参数
params = {
@@ -403,12 +405,12 @@ class MiaoStarsBasicApi:
def getPolicy(self):
"""获取用户存储策略"""
url = "/user/policies"
url = "/user/setting/policies"
r = self.request("GET", url)
# 转换响应格式
if isinstance(r, list):
return {"code": 0, "data": r}
return r
else:
return {"code": 0, "data": []}
@@ -427,11 +429,28 @@ class MiaoStarsBasicApi:
"""创建文件夹"""
url = "/file/create"
currentPath = policyConfig.returnCurrentPath()
# 根据Cloudreve V4 API规范构建uri参数确保不会有重复斜杠
if currentPath == "/":
# 根据Cloudreve V4 API规范构建uri参数确保不会有重复斜杠和前缀
# 清理currentPath确保不包含cloudreve://my前缀
clean_current_path = currentPath.replace("cloudreve://my", "")
if clean_current_path == "/":
uri = f"cloudreve://my/{name}"
else:
uri = f"cloudreve://my{currentPath}/{name}"
uri = f"cloudreve://my{clean_current_path}/{name}"
# 确保路径格式正确,移除重复的前缀
uri = uri.replace("cloudreve://my/cloudreve://my", "cloudreve://my")
# 更健壮地处理重复文件名的情况
path_parts = uri.split('/')
if len(path_parts) > 1:
# 检查最后一个部分是否是名称
if path_parts[-1] == name:
# 检查倒数第二个部分是否也是名称
if len(path_parts) > 2 and path_parts[-2] == name:
# 移除重复的名称部分
path_parts.pop(-2)
uri = '/'.join(path_parts)
r = self.request("POST", url, json={"uri": uri, "type": "folder", "err_on_conflict": True})
# 转换响应格式
@@ -440,20 +459,26 @@ class MiaoStarsBasicApi:
else:
return {"code": -1, "msg": r.get("msg", "文件夹创建失败")}
def deleteFile(self, fileId, fileType: Literal["file", "dir"]):
"""删除文件"""
url = "/file/delete"
def deleteFile(self, fileUri, fileType: Literal["file", "dir"]):
"""删除文件 (Cloudreve V4 API)"""
url = "/file"
# 现在调用方已经传入了正确格式的URI
logger.debug(f"删除文件URI: {fileUri}")
# 根据Cloudreve V4 API规范使用uris参数列表
deleteData = {
"items": [fileId] if fileType == "file" else [],
"dirs": [fileId] if fileType == "dir" else []
"uris": [fileUri]
}
r = self.request("POST", url, json=deleteData)
r = self.request("DELETE", url, json=deleteData)
# 转换响应格式
if r.get("count") > 0:
# Cloudreve V4 响应格式:{code: 0} 表示成功
if isinstance(r, dict) and r.get("code") == 0:
return {"code": 0, "msg": "删除成功"}
else:
return {"code": -1, "msg": r.get("error", "删除失败")}
error_msg = r.get("msg", r.get("error", "删除失败"))
return {"code": -1, "msg": error_msg}
def wareSearch(
self,
@@ -564,6 +589,213 @@ class MiaoStarsBasicApi:
else:
return {"code": -1, "msg": r.get("error", "文件内容更新失败")}
def getFileUrl(self, fileUri, redirect=False):
"""获取文件临时下载URL (Cloudreve V4 API)"""
url = "/file/url"
# 确保传入的是正确格式的URI
if not fileUri.startswith("cloudreve://"):
logger.error(f"无效的URI格式: {fileUri}必须以cloudreve://开头")
return {"code": -1, "msg": "无效的URI格式"}
# 修复文件名重复问题 - 保留URI编码文件名删除后面的原始文件名
if fileUri and '/' in fileUri:
import urllib.parse
parts = fileUri.split('/')
# 检查是否存在潜在的文件名重复情况
if len(parts) >= 2:
# 解码最后一个部分,检查是否与倒数第二个部分解码后相同
try:
decoded_last = urllib.parse.unquote(parts[-1])
decoded_prev = urllib.parse.unquote(parts[-2])
# 如果解码后相同,或者最后一个部分是原始文件名(没有编码)
if decoded_last == decoded_prev or parts[-1] == decoded_prev:
# 保留编码的文件名部分,移除后面的重复部分
fileUri = '/'.join(parts[:-1])
logger.debug(f"修复了重复文件名的URI: {fileUri}")
except Exception as e:
logger.debug(f"解码URI部分时出错: {str(e)}")
data = {
"uris": [fileUri],
"redirect": redirect,
"download": False # 设置为False以获取预览URL
}
logger.debug(f"获取文件URLURI: {fileUri}")
r = self.request("POST", url, json=data)
# 转换响应格式
# 直接返回API响应让调用者处理不同的返回格式
logger.debug(f"getFileUrl API响应: {r}")
# 如果是成功的响应格式,确保返回完整的响应对象
if isinstance(r, dict):
# 兼容不同的成功响应格式
if "urls" in r and isinstance(r["urls"], list):
# 清理URL中的反引号和多余空格
for url_item in r["urls"]:
if isinstance(url_item, dict) and "url" in url_item:
url_item["url"] = url_item["url"].strip('` ')
return r
elif r.get("code") == 0 and "data" in r:
# Cloudreve标准格式
return r
elif r.get("code") != 0:
# 错误响应
return r
# 如果响应不符合预期格式,返回错误
logger.error(f"getFileUrl响应格式不符合预期: {r}")
return {"code": -1, "msg": "获取文件URL失败响应格式错误"}
def getFileContent(self, fileUri):
"""获取文件内容 (Cloudreve V4 API - PUT /file/content)"""
url = "/file/content"
# 确保传入的是正确格式的URI
if not fileUri.startswith("cloudreve://"):
logger.error(f"无效的URI格式: {fileUri}必须以cloudreve://开头")
return {"code": -1, "msg": "无效的URI格式"}
# 修复文件名重复问题 - 保留URI编码文件名删除后面的原始文件名
if fileUri and '/' in fileUri:
import urllib.parse
parts = fileUri.split('/')
# 检查是否存在潜在的文件名重复情况
if len(parts) >= 2:
# 解码最后一个部分,检查是否与倒数第二个部分解码后相同
try:
decoded_last = urllib.parse.unquote(parts[-1])
decoded_prev = urllib.parse.unquote(parts[-2])
# 如果解码后相同,或者最后一个部分是原始文件名(没有编码)
if decoded_last == decoded_prev or parts[-1] == decoded_prev:
# 保留编码的文件名部分,移除后面的重复部分
fileUri = '/'.join(parts[:-1])
logger.debug(f"getFileContent - 修复了重复文件名的URI: {fileUri}")
except Exception as e:
logger.debug(f"解码URI部分时出错: {str(e)}")
# 构建查询参数
params = {"uri": fileUri}
logger.debug(f"获取文件内容URI: {fileUri}")
try:
# 使用PUT请求获取文件内容
r = self.request("PUT", url, params=params)
# 转换响应格式
if isinstance(r, bytes):
# 直接返回二进制内容
return {"code": 0, "data": r}
elif isinstance(r, dict):
if r.get("code") == 0:
# 确保返回的数据是二进制格式
data = r.get("data")
if isinstance(data, bytes):
return {"code": 0, "data": data}
elif isinstance(data, dict) or isinstance(data, list):
# 如果data是字典或列表说明可能是API响应错误使用URL方式获取
logger.warning(f"获取到的数据是{type(data).__name__}类型不是二进制数据将尝试使用URL方式")
# 尝试使用getFileUrl获取临时URL然后下载内容
url_response = self.getFileUrl(fileUri)
if url_response.get("code") == 0 and "data" in url_response and "urls" in url_response["data"]:
urls = url_response["data"]["urls"]
if urls and isinstance(urls[0], dict) and "url" in urls[0]:
temp_url = urls[0]["url"]
# 使用session获取文件内容
session_response = self.returnSession().get(temp_url, stream=True, timeout=(15, 30))
session_response.raise_for_status()
binary_content = session_response.content
logger.info(f"成功从临时URL获取文件内容大小: {len(binary_content)} 字节")
return {"code": 0, "data": binary_content}
else:
# 尝试将其他类型转换为二进制
try:
if isinstance(data, str):
return {"code": 0, "data": data.encode('utf-8')}
else:
return {"code": 0, "data": str(data).encode('utf-8')}
except Exception:
pass
# 错误响应
return {"code": r.get("code", -1), "msg": r.get("msg", "获取文件内容失败")}
else:
# 未知响应类型,尝试转换为二进制
try:
return {"code": 0, "data": str(r).encode('utf-8')}
except Exception:
return {"code": -1, "msg": f"未知的响应类型: {type(r).__name__}"}
except Exception as e:
logger.error(f"获取文件内容时发生异常: {str(e)}")
return {"code": -1, "msg": f"获取文件内容失败: {str(e)}"}
def updateFileContent(self, fileUri, content, previous_version=None):
"""更新文件内容 (Cloudreve V4 API - PUT /file/content)"""
url = "/file/content"
# 确保传入的是正确格式的URI
if not fileUri.startswith("cloudreve://"):
logger.error(f"无效的URI格式: {fileUri}必须以cloudreve://开头")
return {"code": -1, "msg": "无效的URI格式"}
# 构建查询参数
params = {"uri": fileUri}
if previous_version:
params["previous"] = previous_version
# 设置请求头
headers = {
"Content-Length": str(len(content))
}
logger.debug(f"更新文件内容URI: {fileUri}")
r = self.request("PUT", url, params=params, data=content, headers=headers)
# 转换响应格式
if isinstance(r, dict) and r.get("code") == 0:
return {"code": 0, "data": r.get("data", {})}
else:
error_msg = r.get("msg", r.get("error", "更新文件内容失败"))
return {"code": -1, "msg": error_msg}
def createViewerSession(self, fileUri, viewer_id, preferred_action="view", version=None, parent_uri=None):
"""创建查看器会话 (Cloudreve V4 API - PUT /file/viewerSession)"""
url = "/file/viewerSession"
# 确保传入的是正确格式的URI
if not fileUri.startswith("cloudreve://"):
logger.error(f"无效的URI格式: {fileUri}必须以cloudreve://开头")
return {"code": -1, "msg": "无效的URI格式"}
# 构建请求体
data = {
"uri": fileUri,
"viewer_id": viewer_id,
"preferred_action": preferred_action
}
# 添加可选参数
if version:
data["version"] = version
if parent_uri:
data["parent_uri"] = parent_uri
logger.debug(f"创建查看器会话URI: {fileUri}, viewer_id: {viewer_id}")
r = self.request("PUT", url, json=data)
# 转换响应格式
if isinstance(r, dict) and r.get("code") == 0:
return {"code": 0, "data": r.get("data", {})}
else:
error_msg = r.get("msg", r.get("error", "创建查看器会话失败"))
return {"code": -1, "msg": error_msg}
def updateUserNickname(self, nickName):
"""更新用户昵称 (Cloudreve V4 API)"""
url = "/user/profile"
@@ -577,3 +809,7 @@ class MiaoStarsBasicApi:
return {"code": 0, "msg": "昵称更新成功"}
else:
return {"code": -1, "msg": r.get("error", "昵称更新失败")}
# 创建全局实例,方便其他模块直接使用
basicApi = MiaoStarsBasicApi()