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

@@ -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)