|
@@ -0,0 +1,239 @@
|
|
|
+//
|
|
|
+// TSGeneratePhotoOperation.swift
|
|
|
+// AIRingtone
|
|
|
+//
|
|
|
+// Created by 100Years on 2025/3/24.
|
|
|
+//
|
|
|
+import Combine
|
|
|
+import Alamofire
|
|
|
+class TSGeneratePhotoOperationQueue: TSBaseOperationQueue {
|
|
|
+ static let shared:TSGeneratePhotoOperationQueue = TSGeneratePhotoOperationQueue()
|
|
|
+
|
|
|
+
|
|
|
+ // 存储每个操作的 AnyCancellable
|
|
|
+ private var stateables: [String: AnyCancellable] = [:]
|
|
|
+
|
|
|
+ var photoOperationStateChanged:((String)->Void)?
|
|
|
+
|
|
|
+
|
|
|
+ func creatOperation(uuid: String) -> TSGeneratePhotoOperation {
|
|
|
+ let operation = super.creatOperation(uuid: uuid, type: TSGeneratePhotoOperation.self)
|
|
|
+ if let photoOperation = operation as? TSGeneratePhotoOperation {
|
|
|
+ stateables[uuid] = photoOperation.$stateDatauPblished.sink { [weak self] state in
|
|
|
+ guard let self = self else { return }
|
|
|
+ DispatchQueue.main.async {
|
|
|
+ self.photoOperationStateChanged?(uuid)
|
|
|
+
|
|
|
+ let uuidData = self.getUUIDData(uuid: uuid)
|
|
|
+ NotificationCenter.default.post(
|
|
|
+ name: .kGeneratePhotoOperationChanged,
|
|
|
+ object: nil,
|
|
|
+ userInfo: [
|
|
|
+ "uuid": uuid,
|
|
|
+ "count":self.queue.maxConcurrentOperationCount,
|
|
|
+ "state":uuidData.0,
|
|
|
+ "actionInfo":uuidData.1,
|
|
|
+ ])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return operation as! TSGeneratePhotoOperation
|
|
|
+ }
|
|
|
+
|
|
|
+ func getUUIDData(uuid:String)->(TSProgressState,TSActionInfoModel?){
|
|
|
+ if let photoOperation = TSGeneratePhotoOperationQueue.shared.findOperation(uuid: uuid) as? TSGeneratePhotoOperation {
|
|
|
+ dePrint("TSBaseOperation stateDatauPblished 发送 = \(photoOperation.stateDatauPblished)")
|
|
|
+ return (photoOperation.stateDatauPblished.0,photoOperation.currentActionInfoModel)
|
|
|
+ }
|
|
|
+ return (.none,TSActionInfoModel())
|
|
|
+ }
|
|
|
+
|
|
|
+ override func cancelOperations(uuid: String) {
|
|
|
+ super.cancelOperations(uuid: uuid)
|
|
|
+ stateables.removeValue(forKey: uuid)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class TSGeneratePhotoOperation: TSBaseOperation , @unchecked Sendable{
|
|
|
+
|
|
|
+ let actionInfoDictPhoto:[String:Any] = [
|
|
|
+ "actionType":"image_create",
|
|
|
+ "comments": "Success",
|
|
|
+ "costTime":7,
|
|
|
+ "createdTimestamp":1742183588,
|
|
|
+ "id":2450,
|
|
|
+ "percent":1,
|
|
|
+ "request":"{\"prompt\": \"Steampunk floating library with brass gears and clockwork mechanisms, leather-bound books flying through amber-lit fog, Victorian-era architecture blended with retro-futurism, retro anime cel-shading, 1980s Toei Animation style, bold black outlines, limited color palette (--color 1980s_anime), visible film grain --edge_threshold 0.4 --cel_shading 0.8, Retain the original stone size of the photo\", \"width\": 800, \"height\": 800, \"countryCode\": \"US\"}",
|
|
|
+ "response":"{\"resultUrl\": \"https://be-aigc.s3-accelerate.amazonaws.com/8b7fcac9-c691-4c3a-b497-401204fad3e9.png\"}",
|
|
|
+ "status":"success"
|
|
|
+ ]
|
|
|
+
|
|
|
+ @Published var stateDatauPblished:(TSProgressState,TSActionInfoModel?) = (TSProgressState.none,nil){
|
|
|
+ didSet{
|
|
|
+ dePrint("TSBaseOperation stateDatauPblished didSet = \(stateDatauPblished)")
|
|
|
+ if case .start = stateDatauPblished.0 {
|
|
|
+ start()
|
|
|
+ }else if stateDatauPblished.0.isResult {
|
|
|
+ finished()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var gennerateType:TSGennerateType = .photo
|
|
|
+ private var creatRequest:Request?
|
|
|
+ private var queryRequest:Request?
|
|
|
+ private var stopNetwork = false
|
|
|
+ private var generatingProgress = 0
|
|
|
+ private var aiText:String = ""
|
|
|
+ private var action_id:Int = 0
|
|
|
+
|
|
|
+ var currentActionInfoModelChanged:((TSActionInfoModel)->Void)?
|
|
|
+ @Published var currentActionInfoModel: TSActionInfoModel = TSActionInfoModel()
|
|
|
+
|
|
|
+ func replaceSaveInfoModel(model:TSActionInfoModel){
|
|
|
+ model.uuid = uuid
|
|
|
+ TSPhotoHistory.replaceModel(oldID: currentActionInfoModel.id, newModel: model)
|
|
|
+ currentActionInfoModel = model
|
|
|
+ dePrint("TSPhotoHistory.listModelArray.count=\(TSPhotoHistory.listModelArray.count)")
|
|
|
+ dePrint("model actionStatus 发出=\(model.actionStatus)")
|
|
|
+ currentActionInfoModelChanged?(currentActionInfoModel)
|
|
|
+ }
|
|
|
+
|
|
|
+ //模拟数据
|
|
|
+ func creatPhoto(oldModel:TSActionInfoModel? = nil,prompt:String,promptSort:String) {
|
|
|
+ stateDatauPblished = (.start,nil)
|
|
|
+ if let model = oldModel {
|
|
|
+ currentActionInfoModel = model
|
|
|
+ }else {
|
|
|
+ currentActionInfoModel.id = Int.timestampInt()
|
|
|
+ currentActionInfoModel.request.prompt = prompt
|
|
|
+ currentActionInfoModel.request.promptSort = promptSort
|
|
|
+ currentActionInfoModel.actionStatus = .pending
|
|
|
+ currentActionInfoModel.status = "pending"
|
|
|
+ }
|
|
|
+
|
|
|
+ replaceSaveInfoModel(model: currentActionInfoModel)
|
|
|
+ stateDatauPblished = (.start,currentActionInfoModel)
|
|
|
+ let time = 5.0
|
|
|
+
|
|
|
+ for i in 0..<Int(time){
|
|
|
+ kDelayOnMainThread(Double(i)) {
|
|
|
+ let progress = Float(i)/100.0
|
|
|
+ self.currentActionInfoModel.percent = progress
|
|
|
+ self.currentActionInfoModel.actionStatus = .running
|
|
|
+ self.currentActionInfoModel.status = "running"
|
|
|
+ self.replaceSaveInfoModel(model: self.currentActionInfoModel)
|
|
|
+ self.stateDatauPblished = (.progressString(self.generating(progress: progress)),nil)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ kDelayOnMainThread(time+1.0) {
|
|
|
+ if Bool.random(), let infoModel = TSActionInfoModel(JSON: self.actionInfoDictPhoto){
|
|
|
+ infoModel.id = Int.uuid
|
|
|
+ self.replaceSaveInfoModel(model: infoModel)
|
|
|
+ self.stateDatauPblished = (.success(nil),self.currentActionInfoModel)
|
|
|
+ }else{
|
|
|
+ self.currentActionInfoModel.actionStatus = .failed
|
|
|
+ self.currentActionInfoModel.status = "failed"
|
|
|
+ self.replaceSaveInfoModel(model: self.currentActionInfoModel)
|
|
|
+ self.stateDatauPblished = (.failed("error?.localizedDescription"),nil)
|
|
|
+ }
|
|
|
+// TSPhotoHistory.dePrintAllModel()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// //width 和 height 必须是 32 的倍数
|
|
|
+// func creatImageEmoji(text:String) {
|
|
|
+// generatingProgress = 0
|
|
|
+// aiText = text
|
|
|
+// let postDict:[String : Any] = [
|
|
|
+// "prompt":text,
|
|
|
+// "width":textPicW,
|
|
|
+// "height":textPicH
|
|
|
+// ]
|
|
|
+// stateDatauPblished = (.start,nil)
|
|
|
+// stateDatauPblished = (.progressString(generating(progress: 0.0)),nil)
|
|
|
+// creatRequest = TSNetworkShared.post(urlType: .textPicCreate,parameters: postDict) { [weak self] data,error in
|
|
|
+// guard let self = self else { return }
|
|
|
+// if let dataDict = data as? [String:Any] ,
|
|
|
+// dataDict.safeInt(forKey: "code") == 200,
|
|
|
+// let actionId = dataDict["actionId"] as? Int{
|
|
|
+// if stopNetwork == false {
|
|
|
+// self.getActionInfo(action_id:actionId)
|
|
|
+// }
|
|
|
+// }else{
|
|
|
+// self.stateDatauPblished = (.failed(error?.localizedDescription ?? ""),nil)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+ func getActionInfo(action_id:Int){
|
|
|
+ queryRequest = TSNetworkShared.get(urlType: .actionInfo,parameters: ["action_id":action_id]) { [weak self] data,error in
|
|
|
+ guard let self = self else { return }
|
|
|
+ if let result = kNetWorkResultSuccess(data: data) {
|
|
|
+ if let genmojiModel = TSActionInfoModel(JSON: result) {
|
|
|
+ switch genmojiModel.actionStatus {
|
|
|
+ case .success:
|
|
|
+ TSToastShared.hideLoading()
|
|
|
+ self.stateDatauPblished = (.success(nil),genmojiModel)
|
|
|
+ generatingProgress = 0
|
|
|
+ case .failed:
|
|
|
+ self.stateDatauPblished = (.failed(kNetWorkMessage(data: data) ?? ""),nil)
|
|
|
+ generatingProgress = 0
|
|
|
+ default:
|
|
|
+ stateDatauPblished = (.progressString(generating(progress: genmojiModel.percent)),nil)
|
|
|
+ if stopNetwork == false {
|
|
|
+ kDelayOnMainThread(1.0) {
|
|
|
+ self.getActionInfo(action_id: action_id)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ self.stateDatauPblished = (.failed(error?.localizedDescription ?? ""),nil)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func cancelAllRequest(){
|
|
|
+ creatRequest?.cancel()
|
|
|
+ queryRequest?.cancel()
|
|
|
+ stopNetwork = true
|
|
|
+ }
|
|
|
+
|
|
|
+ func generating(progress:Float) -> String {
|
|
|
+
|
|
|
+ //Generating 0%-100%
|
|
|
+ var progressInt = Int(progress*100)
|
|
|
+
|
|
|
+ if generatingProgress >= progressInt{
|
|
|
+ return generatingString
|
|
|
+ }
|
|
|
+
|
|
|
+ if progressInt > 99 {
|
|
|
+ progressInt = 99
|
|
|
+ }
|
|
|
+
|
|
|
+ generatingProgress = progressInt
|
|
|
+ return generatingString
|
|
|
+ }
|
|
|
+
|
|
|
+ var generatingString:String {
|
|
|
+ if gennerateType == .photo {
|
|
|
+ return "Working on your contact photo \(generatingProgress)% ..."
|
|
|
+ }
|
|
|
+ return "Working on your contact Photo \(generatingProgress)% ..."
|
|
|
+ }
|
|
|
+
|
|
|
+ var textPicW:Int{
|
|
|
+ return kTextPicW
|
|
|
+ }
|
|
|
+
|
|
|
+ var textPicH:Int{
|
|
|
+ if gennerateType == .photo {
|
|
|
+ return kTextPicW
|
|
|
+ }
|
|
|
+ return kTextPicH
|
|
|
+ }
|
|
|
+}
|