fix lot of thing
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import os
|
||||
import logging
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import requests
|
||||
from PyQt6.QtCore import QThread, pyqtSignal
|
||||
from PyQt6.QtGui import QImage, QPixmap
|
||||
|
||||
from app.core import miaoStarsBasicApi
|
||||
from app.core.api import basicApi, miaoStarsBasicApi
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TextLoaderThread(QThread):
|
||||
@@ -15,54 +18,88 @@ class TextLoaderThread(QThread):
|
||||
errorOccurred = pyqtSignal(str)
|
||||
progressUpdated = pyqtSignal(int) # 进度更新信号
|
||||
|
||||
def __init__(self, url):
|
||||
def __init__(self, url, fileId=None):
|
||||
super().__init__()
|
||||
self.url = url
|
||||
self.url = url # 原始URL,可能作为fallback
|
||||
self.fileId = fileId # 文件ID,优先使用
|
||||
|
||||
def run(self):
|
||||
"""线程执行函数"""
|
||||
try:
|
||||
# 1. 设置网络请求参数 - 优化连接参数
|
||||
session = requests.Session()
|
||||
adapter = requests.adapters.HTTPAdapter(
|
||||
pool_connections=20,
|
||||
pool_maxsize=20,
|
||||
max_retries=5, # 增加重试次数
|
||||
pool_block=False,
|
||||
)
|
||||
session.mount("http://", adapter)
|
||||
session.mount("https://", adapter)
|
||||
binary_content = None
|
||||
content_chunks = [] # 预先初始化content_chunks,避免在任何执行路径下出现未定义错误
|
||||
|
||||
# 如果提供了fileId(实际上是URI),优先使用getFileContent直接获取内容
|
||||
if self.fileId:
|
||||
self.progressUpdated.emit(0) # 发送初始进度
|
||||
logger.info(f"使用文件URI {self.fileId} 直接获取文件内容")
|
||||
response = basicApi.getFileContent(self.fileId)
|
||||
|
||||
if response.get('code') == 0 and 'data' in response:
|
||||
# 检查response['data']的类型,确保只有二进制数据才直接使用
|
||||
if isinstance(response['data'], bytes):
|
||||
binary_content = response['data']
|
||||
logger.info(f"成功获取文件内容,大小: {len(binary_content)} 字节")
|
||||
self.progressUpdated.emit(100) # 直接发送完成进度
|
||||
else:
|
||||
# 如果不是二进制数据(可能是字典等其他类型),记录警告并使用URL作为fallback
|
||||
logger.warning(f"获取到的文件内容不是二进制数据,类型: {type(response['data']).__name__},将使用URL作为fallback")
|
||||
binary_content = None
|
||||
else:
|
||||
error_msg = response.get('msg', '获取文件内容失败')
|
||||
logger.warning(f"获取文件内容失败: {error_msg},将使用原始URL作为fallback")
|
||||
|
||||
# 如果直接获取内容失败,尝试使用URL
|
||||
if binary_content is None:
|
||||
if not self.url:
|
||||
self.errorOccurred.emit("没有可用的URL来加载文本内容")
|
||||
return
|
||||
|
||||
# 1. 设置网络请求参数 - 优化连接参数
|
||||
session = requests.Session()
|
||||
adapter = requests.adapters.HTTPAdapter(
|
||||
pool_connections=20,
|
||||
pool_maxsize=20,
|
||||
max_retries=5, # 增加重试次数
|
||||
pool_block=False,
|
||||
)
|
||||
session.mount("http://", adapter)
|
||||
session.mount("https://", adapter)
|
||||
|
||||
# 2. 增加超时时间并添加重试机制
|
||||
response = miaoStarsBasicApi.returnSession().get(
|
||||
self.url,
|
||||
stream=True,
|
||||
timeout=(15, 30), # 增加超时时间:连接15秒,读取30秒
|
||||
)
|
||||
# 2. 增加超时时间并添加重试机制
|
||||
response = basicApi.returnSession().get(
|
||||
self.url,
|
||||
stream=True,
|
||||
timeout=(15, 30), # 增加超时时间:连接15秒,读取30秒
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
response.raise_for_status()
|
||||
|
||||
# 3. 获取文件大小(如果服务器支持)
|
||||
total_size = int(response.headers.get("content-length", 0))
|
||||
downloaded_size = 0
|
||||
# 3. 获取文件大小(如果服务器支持)
|
||||
total_size = int(response.headers.get("content-length", 0))
|
||||
downloaded_size = 0
|
||||
|
||||
# 4. 分块读取并处理 - 使用二进制读取提高速度
|
||||
content_chunks = []
|
||||
for chunk in response.iter_content(chunk_size=16384): # 增大块大小
|
||||
if chunk:
|
||||
content_chunks.append(chunk)
|
||||
downloaded_size += len(chunk)
|
||||
# 4. 分块读取并处理 - 使用二进制读取提高速度
|
||||
content_chunks = []
|
||||
for chunk in response.iter_content(chunk_size=16384): # 增大块大小
|
||||
if chunk:
|
||||
content_chunks.append(chunk)
|
||||
downloaded_size += len(chunk)
|
||||
|
||||
# 更新进度(如果知道总大小)
|
||||
if total_size > 0:
|
||||
progress = int((downloaded_size / total_size) * 100)
|
||||
self.progressUpdated.emit(progress)
|
||||
# 更新进度(如果知道总大小)
|
||||
if total_size > 0:
|
||||
progress = int((downloaded_size / total_size) * 100)
|
||||
self.progressUpdated.emit(progress)
|
||||
|
||||
# 5. 合并内容并解码
|
||||
binary_content = b"".join(content_chunks)
|
||||
# 5. 合并内容 - 使用正确的二进制合并语法
|
||||
binary_content = b"" .join(content_chunks)
|
||||
|
||||
if not binary_content:
|
||||
self.errorOccurred.emit("下载内容为空")
|
||||
# 确保binary_content是bytes类型且不为空
|
||||
if not binary_content or not isinstance(binary_content, bytes):
|
||||
if not binary_content:
|
||||
self.errorOccurred.emit("下载内容为空")
|
||||
else:
|
||||
self.errorOccurred.emit(f"下载内容类型错误,应为bytes,实际为: {type(binary_content).__name__}")
|
||||
return
|
||||
|
||||
# 6. 智能编码检测和解码
|
||||
@@ -113,10 +150,11 @@ class ImageLoaderThread(QThread):
|
||||
progressUpdated = pyqtSignal(int) # 进度更新信号
|
||||
|
||||
def __init__(
|
||||
self, url, cache_dir="image_cache", max_cache_size=50 * 1024 * 1024
|
||||
self, url, cache_dir="image_cache", max_cache_size=50 * 1024 * 1024, fileId=None
|
||||
): # 50MB缓存
|
||||
super().__init__()
|
||||
self.url = url
|
||||
self.url = url # 原始URL,可能作为fallback
|
||||
self.fileId = fileId # 文件ID,优先使用
|
||||
self.cache_dir = cache_dir
|
||||
self.max_cache_size = max_cache_size
|
||||
self._setup_cache()
|
||||
@@ -184,48 +222,108 @@ class ImageLoaderThread(QThread):
|
||||
def run(self):
|
||||
"""线程执行函数"""
|
||||
try:
|
||||
# 1. 首先检查缓存
|
||||
cached_pixmap = self._get_cached_image()
|
||||
if cached_pixmap:
|
||||
self.imageLoaded.emit(cached_pixmap)
|
||||
return
|
||||
image_data = None
|
||||
|
||||
# 如果提供了fileId(实际上是URI),优先使用getFileContent直接获取内容
|
||||
if self.fileId:
|
||||
logger.info(f"使用文件URI {self.fileId} 直接获取图片内容")
|
||||
response = miaoStarsBasicApi.getFileContent(self.fileId)
|
||||
|
||||
if response.get('code') == 0 and 'data' in response:
|
||||
# 检查response['data']的类型,确保只有二进制数据才直接使用
|
||||
if isinstance(response['data'], bytes):
|
||||
image_data = response['data']
|
||||
logger.info(f"成功获取图片内容,大小: {len(image_data)} 字节")
|
||||
self.progressUpdated.emit(100) # 直接发送完成进度
|
||||
else:
|
||||
# 如果不是二进制数据(可能是字典等其他类型),记录警告并使用URL作为fallback
|
||||
logger.warning(f"获取到的图片内容不是二进制数据,类型: {type(response['data']).__name__},将使用URL作为fallback")
|
||||
else:
|
||||
error_msg = response.get('msg', '获取图片内容失败')
|
||||
logger.warning(f"获取图片内容失败: {error_msg},将使用原始URL作为fallback")
|
||||
|
||||
# 如果直接获取内容失败,尝试使用URL
|
||||
if image_data is None:
|
||||
if not self.url:
|
||||
self.errorOccurred.emit("没有可用的URL来加载图片内容")
|
||||
return
|
||||
|
||||
# 确保URL是字符串类型
|
||||
if isinstance(self.url, dict):
|
||||
# 处理字典类型的URL,通常包含urls数组
|
||||
if 'urls' in self.url and isinstance(self.url['urls'], list) and len(self.url['urls']) > 0:
|
||||
# 获取第一个URL信息
|
||||
url_info = self.url['urls'][0]
|
||||
if isinstance(url_info, dict) and 'url' in url_info:
|
||||
download_url = url_info['url']
|
||||
logger.info(f"从字典中提取URL: {download_url}")
|
||||
self.url = download_url
|
||||
else:
|
||||
logger.error("URL字典格式不正确,缺少有效的URL信息")
|
||||
self.errorOccurred.emit("URL格式错误: 缺少有效的URL信息")
|
||||
return
|
||||
else:
|
||||
logger.error(f"URL字典格式不正确,缺少urls数组或数组为空: {self.url}")
|
||||
self.errorOccurred.emit("URL格式错误: 缺少有效的URL信息")
|
||||
return
|
||||
elif not isinstance(self.url, str):
|
||||
logger.error(f"URL类型错误,应为字符串或字典,实际为: {type(self.url).__name__}")
|
||||
self.errorOccurred.emit(f"URL类型错误: 应为字符串")
|
||||
return
|
||||
|
||||
# 1. 首先检查缓存
|
||||
cached_pixmap = self._get_cached_image()
|
||||
if cached_pixmap:
|
||||
self.imageLoaded.emit(cached_pixmap)
|
||||
return
|
||||
|
||||
# 2. 设置更短的超时时间
|
||||
session = requests.Session()
|
||||
adapter = requests.adapters.HTTPAdapter(
|
||||
pool_connections=10, pool_maxsize=10, max_retries=5 # 重试2次
|
||||
)
|
||||
session.mount("http://", adapter)
|
||||
session.mount("https://", adapter)
|
||||
# 2. 设置更短的超时时间
|
||||
session = requests.Session()
|
||||
adapter = requests.adapters.HTTPAdapter(
|
||||
pool_connections=10, pool_maxsize=10, max_retries=5 # 重试2次
|
||||
)
|
||||
session.mount("http://", adapter)
|
||||
session.mount("https://", adapter)
|
||||
|
||||
# 3. 流式下载,支持进度显示
|
||||
response = miaoStarsBasicApi.returnSession().get(
|
||||
self.url, stream=True, timeout=(20, 30) # 连接超时5秒,读取超时10秒
|
||||
)
|
||||
response.raise_for_status()
|
||||
# 3. 流式下载,支持进度显示
|
||||
response = miaoStarsBasicApi.returnSession().get(
|
||||
self.url, stream=True, timeout=(20, 30) # 连接超时5秒,读取超时10秒
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
# 获取文件大小(如果服务器支持)
|
||||
total_size = int(response.headers.get("content-length", 0))
|
||||
downloaded_size = 0
|
||||
# 获取文件大小(如果服务器支持)
|
||||
total_size = int(response.headers.get("content-length", 0))
|
||||
downloaded_size = 0
|
||||
|
||||
# 4. 分块读取并处理
|
||||
image_data = b""
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
if chunk:
|
||||
image_data += chunk
|
||||
downloaded_size += len(chunk)
|
||||
# 4. 分块读取并处理
|
||||
image_data = b""
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
if chunk:
|
||||
image_data += chunk
|
||||
downloaded_size += len(chunk)
|
||||
|
||||
# 更新进度(如果知道总大小)
|
||||
if total_size > 0:
|
||||
progress = int((downloaded_size / total_size) * 100)
|
||||
self.progressUpdated.emit(progress)
|
||||
# 更新进度(如果知道总大小)
|
||||
if total_size > 0:
|
||||
# 确保进度不超过100%
|
||||
progress = min(int((downloaded_size / total_size) * 100), 100)
|
||||
self.progressUpdated.emit(progress)
|
||||
|
||||
# 5. 从数据创建QImage(比QPixmap更快)
|
||||
image = QImage()
|
||||
image.loadFromData(image_data)
|
||||
|
||||
if image.isNull():
|
||||
raise Exception("无法加载图片数据")
|
||||
|
||||
# 检查数据是否为空或类型是否正确
|
||||
if not image_data:
|
||||
raise Exception("图片数据为空")
|
||||
|
||||
if not isinstance(image_data, bytes):
|
||||
logger.error(f"图片数据类型错误,应为bytes,实际为: {type(image_data).__name__}")
|
||||
raise Exception(f"图片数据类型错误: 应为bytes,实际为{type(image_data).__name__}")
|
||||
|
||||
# 尝试加载图片数据
|
||||
load_success = image.loadFromData(image_data)
|
||||
|
||||
if not load_success or image.isNull():
|
||||
raise Exception("无法加载图片数据或图片格式不支持")
|
||||
|
||||
# 6. 转换为QPixmap
|
||||
pixmap = QPixmap.fromImage(image)
|
||||
|
||||
Reference in New Issue
Block a user