optimize the appicon of iOS

This commit is contained in:
2026-08-26 17:14:59 +08:00
parent 76b0332035
commit bb677ea8cf
11 changed files with 406 additions and 11 deletions

View File

@@ -15,6 +15,13 @@ enum ImitationAssessorError: LocalizedError {
}
}
struct DubShareUploadRequest {
let videoHash: String
let title: String
let segments: [(index: Int, audioURL: URL)]
let scores: [Int: ImitationAssessmentResult]
}
enum ImitationAssessor {
/// SHA-256
static func sha256Hex(of fileURL: URL) throws -> String {
@@ -100,6 +107,96 @@ enum ImitationAssessor {
return try JSONDecoder().decode(ImitationAssessmentResult.self, from: result.body)
}
static func uploadDubShare(request: DubShareUploadRequest) async throws -> String {
let hash = request.videoHash.lowercased()
guard hash.range(of: AppConfig.sha256Pattern, options: .regularExpression) != nil else {
throw ImitationAssessorError.invalidHash
}
guard !request.segments.isEmpty else {
throw ImitationAssessorError.cannotReadRecording
}
let boundary = "----OralTrainer-\(UUID().uuidString)"
var body = Data()
appendField(&body, boundary: boundary, name: "video_hash", value: hash)
appendField(&body, boundary: boundary, name: "title", value: request.title.isEmpty ? "我的口语配音" : request.title)
let segmentItems = request.segments
.sorted { $0.index < $1.index }
.map { "{\"sentence_index\":\($0.index)}" }
appendField(&body, boundary: boundary, name: "segments", value: "[\(segmentItems.joined(separator: ","))]")
let scoreItems = request.segments
.sorted { $0.index < $1.index }
.compactMap { segment -> String? in
guard let score = request.scores[segment.index] else { return nil }
var fields = ["\"sentence_index\":\(segment.index)"]
for (key, value) in [
("overall_score", score.overallScore as Float?),
("content_score", score.contentScore),
("fluency_score", score.fluencyScore),
("duration_score", score.durationScore),
("pause_score", score.pauseScore),
("speech_rate_score", score.speechRateScore),
] {
if let value = value {
fields.append("\"\(key)\":\(value)")
}
}
if let text = score.recognizedText {
let escaped = text
.replacingOccurrences(of: "\\", with: "\\\\")
.replacingOccurrences(of: "\"", with: "\\\"")
fields.append("\"recognized_text\":\"\(escaped)\"")
}
return "{\(fields.joined(separator: ","))}"
}
appendField(&body, boundary: boundary, name: "scores", value: "[\(scoreItems.joined(separator: ","))]")
for segment in request.segments.sorted(by: { $0.index < $1.index }) {
let audioData: Data
do {
audioData = try Data(contentsOf: segment.audioURL)
} catch {
throw ImitationAssessorError.cannotReadRecording
}
appendFile(
&body,
boundary: boundary,
name: "files",
filename: "dub-\(segment.index).m4a",
mimeType: "audio/mp4",
data: audioData
)
}
body.append(Data("--\(boundary)--\r\n".utf8))
let result = try await HTTPClient.execute(
baseURL: AppConfig.serverBaseURL,
path: "api/v1/dub-shares",
method: "POST",
timeout: 120,
headers: [
"Accept": "application/json",
"Content-Type": "multipart/form-data; boundary=\(boundary)",
],
body: body
)
guard (200...299).contains(result.statusCode) else {
let responseBody = String(data: result.body, encoding: .utf8) ?? ""
throw HTTPClientError.badStatus(result.statusCode, responseBody)
}
struct ShareResponse: Decodable {
let shareId: String
private enum CodingKeys: String, CodingKey {
case shareId = "share_id"
}
}
let response = try JSONDecoder().decode(ShareResponse.self, from: result.body)
let base = AppConfig.serverBaseURL.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
return "\(base)/dub-shares/\(response.shareId)"
}
private static func appendField(_ body: inout Data, boundary: String, name: String, value: String) {
body.append(Data("--\(boundary)\r\n".utf8))
body.append(Data("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n".utf8))