fix: 修复签名不匹配的根本原因,改用手动拼接 URL 替代 URLComponents,增加加载重试机制
CI / lint-and-build (push) Has been cancelled

This commit is contained in:
2026-06-30 12:12:06 +06:00
parent 2a53f6a862
commit 5a13a8e6a6
3 changed files with 54 additions and 34 deletions
@@ -66,15 +66,12 @@ actor VODClient {
params["Signature"] = signature params["Signature"] = signature
let host = endpoint() let host = endpoint()
var components = URLComponents() let query = params.sorted(by: { $0.key < $1.key })
components.scheme = "https" .map { "\(VODSignature.percentEncodePublic($0.key))=\(VODSignature.percentEncodePublic($0.value))" }
components.host = host .joined(separator: "&")
components.path = "/" let urlString = "https://\(host)/?\(query)"
components.queryItems = params.sorted(by: { $0.key < $1.key }).map {
URLQueryItem(name: $0.key, value: $0.value)
}
guard let url = components.url else { guard let url = URL(string: urlString) else {
throw VODClientError.invalidResponse throw VODClientError.invalidResponse
} }
return url return url
@@ -93,7 +90,17 @@ actor VODClient {
} }
let decoder = JSONDecoder() let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data) do {
return try decoder.decode(T.self, from: data)
} catch {
if action == "GetPlayInfo" {
print("[VODClient] GetPlayInfo 解码失败: \(error)")
if let raw = String(data: data, encoding: .utf8) {
print("[VODClient] GetPlayInfo 原始响应: \(raw)")
}
}
throw error
}
} }
func searchMedia( func searchMedia(
@@ -26,8 +26,6 @@ struct VODSignature {
let stringToSign = "\(method)&%2F&\(percentEncode(canonicalQuery))" let stringToSign = "\(method)&%2F&\(percentEncode(canonicalQuery))"
print("[VODSignature] stringToSign: \(stringToSign)")
guard let keyData = "\(accessKeySecret)&".data(using: .utf8) else { guard let keyData = "\(accessKeySecret)&".data(using: .utf8) else {
throw VODSignatureError.invalidKey throw VODSignatureError.invalidKey
} }
@@ -41,6 +39,10 @@ struct VODSignature {
return signatureData.base64EncodedString() return signatureData.base64EncodedString()
} }
static func percentEncodePublic(_ value: String) -> String {
percentEncode(value)
}
private static func percentEncode(_ value: String) -> String { private static func percentEncode(_ value: String) -> String {
let allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~" let allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"
var result = "" var result = ""
@@ -243,31 +243,42 @@ struct VideoListView: View {
private func refreshFromAPI() async { private func refreshFromAPI() async {
isLoading = true isLoading = true
errorMessage = nil errorMessage = nil
loadingStatus = "正在加载视频列表..." let maxRetries = 3
var collected: [VideoItem] = [] for attempt in 1...maxRetries {
var page = 1 loadingStatus = attempt == 1 ? "正在加载视频列表..." : "重试中... (第 \(attempt)/\(maxRetries) 次)"
let size = 100 var collected: [VideoItem] = []
var total = 0 var page = 1
do { let size = 100
while true { var total = 0
loadingStatus = "正在加载视频列表... (第 \(page) 页)" do {
let response = try await VODClient.shared.searchMedia( while true {
pageNo: page, loadingStatus = "正在加载视频列表... (第 \(page) 页)"
pageSize: size let response = try await VODClient.shared.searchMedia(
) pageNo: page,
let videos = response.videos pageSize: size
collected.append(contentsOf: videos) )
total = response.Total ?? 0 let videos = response.videos
if collected.count >= total || videos.isEmpty { collected.append(contentsOf: videos)
break total = response.Total ?? 0
if collected.count >= total || videos.isEmpty {
break
}
page += 1
try? await Task.sleep(for: .milliseconds(200))
}
allVideos = collected
applyFilter()
loadingStatus = ""
isLoading = false
return
} catch {
if attempt < maxRetries {
let delay = UInt64(attempt) * 1_000_000_000
try? await Task.sleep(nanoseconds: delay)
} else {
errorMessage = "加载失败(已重试 \(maxRetries) 次):\(error.localizedDescription)"
} }
page += 1
} }
allVideos = collected
applyFilter()
loadingStatus = ""
} catch {
errorMessage = "加载失败:\(error.localizedDescription)"
} }
isLoading = false isLoading = false
} }