|
@@ -0,0 +1,259 @@
|
|
|
+//
|
|
|
+// TSGenerateBasePhotoOperation.swift
|
|
|
+// AIRingtone
|
|
|
+//
|
|
|
+// Created by 100Years on 2025/3/24.
|
|
|
+//
|
|
|
+
|
|
|
+import Combine
|
|
|
+import Alamofire
|
|
|
+import ObjectMapper
|
|
|
+import Kingfisher
|
|
|
+class TSGenerateBasePhotoOperationQueue: TSGenerateBaseOperationQueue {
|
|
|
+ static let shared:TSGenerateBasePhotoOperationQueue = TSGenerateBasePhotoOperationQueue()
|
|
|
+
|
|
|
+ func creatOperation(uuid: String) -> TSGenerateBasePhotoOperation {
|
|
|
+ let operation = super.creatOperation(uuid: uuid, type: TSGenerateBasePhotoOperation.self)
|
|
|
+ handleStateDatauPblished(uuid: uuid, generateOperation: operation as! TSGenerateBaseOperation, notificationName: .kGenerateBasePhotoOperation)
|
|
|
+ return operation as! TSGenerateBasePhotoOperation
|
|
|
+ }
|
|
|
+
|
|
|
+ override func getUUIDData(uuid:String)->(TSProgressState,TSActionInfoModel?){
|
|
|
+ if let PosterOperation = TSGenerateBasePhotoOperationQueue.shared.findOperation(uuid: uuid) as? TSGenerateBasePhotoOperation {
|
|
|
+ dePrint("TSBaseOperation stateDatauPblished 发送 = \(PosterOperation.stateDatauPblished)")
|
|
|
+ return (PosterOperation.stateDatauPblished.0,PosterOperation.currentActionInfoModel)
|
|
|
+ }
|
|
|
+ return (.none,TSActionInfoModel())
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+class TSGenerateBasePhotoOperation: TSGenerateBaseOperation , @unchecked Sendable{
|
|
|
+
|
|
|
+ //是否展示成功后的 View 提醒
|
|
|
+ public var isShowSuccessView:Bool = false
|
|
|
+
|
|
|
+ override var actionInfoDict:[String:Any]{
|
|
|
+ return [
|
|
|
+ "actionType":"image_create",
|
|
|
+ "comments": "Success",
|
|
|
+ "costTime":9,
|
|
|
+ "createdTimestamp":1742183242,
|
|
|
+ "id":2449,
|
|
|
+ "percent":1,
|
|
|
+ "request":"{\"prompt\": \"Traditional Chinese ink painting style phoenix soaring through misty mountain peaks, dynamic black brushstrokes with crimson accents on rice paper texture, Googie architecture space station, 1950s atomic age aesthetic, curved aluminum panels, starburst patterns, glass dome observatory --edge_threshold 0.5 --atomic_age_style 0.8 --chrome_reflection 1.2\", \"width\": 800, \"height\": 1440, \"countryCode\": \"FR\"}",
|
|
|
+ "response": "{\"resultUrl\": \"https://be-aigc.s3-accelerate.amazonaws.com/4c946f78-b1e7-4ffe-ba18-fff26b10178c.png\"}",
|
|
|
+ "status":"success"
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ override func replaceSaveInfoModel(model:TSActionInfoModel){
|
|
|
+ model.uuid = uuid
|
|
|
+ model.request.imageUrlTimestamp = currentActionInfoModel.request.imageUrlTimestamp
|
|
|
+ if isSaveDB {
|
|
|
+ TSRMShared.aiListDB.updateData(model,id: currentActionInfoModel.id)
|
|
|
+ }
|
|
|
+ currentActionInfoModel = model
|
|
|
+ dePrint("model actionStatus 发出=\(model.actionStatus)")
|
|
|
+ currentActionInfoModelChanged?(currentActionInfoModel)
|
|
|
+ }
|
|
|
+
|
|
|
+ override func handleGenerateSuccess() {
|
|
|
+// kPurchaseBusiness.useOnceForFree(type: .general)
|
|
|
+ TSRMShared.aiListDB.updateData(currentActionInfoModel,id: currentActionInfoModel.id)
|
|
|
+ if isShowSuccessView == false {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ guard let window = WindowHelper.getKeyWindow() else {
|
|
|
+ debugPrint("getKeyWindow nil")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ guard let rootVC = WindowHelper.topViewController() else {
|
|
|
+ debugPrint("handleGenerateSuccess topViewController nil")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let copyModel = self.currentActionInfoModel.copy()
|
|
|
+ if let cyModel = copyModel as? TSActionInfoModel {
|
|
|
+ let topY = k_Nav_Height+10
|
|
|
+ debugPrint("topY=\(topY)")
|
|
|
+ kSaveSuccesswShared.show(atView: window,text: "Successfully generated".localized,deadline: 5.0,bottom: kSaveSuccesswShared.getBottom(topY: topY)) {
|
|
|
+// let gennerateVC = TSPTPGeneratorVC(generateStyleModel: TSAIListPhotoGeneratorModel(),infoModel: cyModel) { model in }
|
|
|
+// gennerateVC.modalPresentationStyle = .overFullScreen
|
|
|
+// gennerateVC.modalTransitionStyle = .crossDissolve
|
|
|
+// rootVC.present(gennerateVC, animated: true)
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ debugPrint("copyModel as? TSActionInfoModel error")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ 1.用户上传图片
|
|
|
+ 2.调用生成接口
|
|
|
+ 3.后台返回查询 id,根据 id 不断查询进度和结果.(此时才允许进度后台)
|
|
|
+
|
|
|
+ 利用3后台返回的TSActionRequestModel中存着关键的请求接口数据,请求数据
|
|
|
+
|
|
|
+ */
|
|
|
+
|
|
|
+ private var uploadRequest:Request?
|
|
|
+ private var creatRequest:Request?
|
|
|
+
|
|
|
+ private func createActionInfoModel(generateStyleModel:TSAIListPhotoGeneratorModel) -> TSActionInfoModel? {
|
|
|
+
|
|
|
+ guard let upLoadImageUrl = generateStyleModel.upLoadImageUrl else { return nil }
|
|
|
+
|
|
|
+ let infoModel = TSActionInfoModel()
|
|
|
+ infoModel.id = Date.timestampInt
|
|
|
+ infoModel.request = TSActionRequestModel()
|
|
|
+ infoModel.request.imageUrl = upLoadImageUrl
|
|
|
+ infoModel.request.imageUrlTimestamp = Date.timestampInt
|
|
|
+
|
|
|
+ infoModel.request.prompt = generateStyleModel.prompt
|
|
|
+ infoModel.request.inputText = generateStyleModel.inputText
|
|
|
+ infoModel.request.generatorStyle = generateStyleModel.generatorStyle
|
|
|
+
|
|
|
+ return infoModel
|
|
|
+ }
|
|
|
+
|
|
|
+ func uploadImage(generateStyleModel:TSAIListPhotoGeneratorModel,complete:@escaping (TSActionInfoModel?)->Void) {
|
|
|
+ let upLoadImage = generateStyleModel.upLoadImage
|
|
|
+ if let imageUrl = generateStyleModel.upLoadImageUrl,imageUrl.contains("http") {
|
|
|
+ complete(createActionInfoModel(generateStyleModel: generateStyleModel))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ stopNetwork = false
|
|
|
+ stateDatauPblished = (.start,nil)
|
|
|
+
|
|
|
+ stateDatauPblished = (.progressString(uploadingPhoto(progress: 0.0)),currentActionInfoModel)
|
|
|
+ uploadRequest = TSNetworkShared.uploadImage(upLoadImage: upLoadImage, maxKb: imageMaxKb) { [weak self] progress in
|
|
|
+ guard let self = self else { return }
|
|
|
+ if generatingProgress == 0 {
|
|
|
+ stateDatauPblished = (.progressString(uploadingPhoto(progress: progress)),currentActionInfoModel)
|
|
|
+ }
|
|
|
+ } completion: { [weak self] data, error in
|
|
|
+ guard let self = self else { return }
|
|
|
+ if stopNetwork == true { return }
|
|
|
+ if let error = error {
|
|
|
+ generateStyleModel.upLoadImageUrl = nil
|
|
|
+ self.stateDatauPblished = (TSProgressState.getFailed(error.tsDesc,error.tsCode),nil)
|
|
|
+ complete(nil)
|
|
|
+ }else{
|
|
|
+ if let string = data as? String {
|
|
|
+ generateStyleModel.upLoadImageUrl = string
|
|
|
+ if let url = URL(string: string){//上传成功后,就将图片缓存到本地
|
|
|
+ ImageCache.default.store(upLoadImage, forKey: url.cacheKey)
|
|
|
+ }
|
|
|
+
|
|
|
+ complete(createActionInfoModel(generateStyleModel: generateStyleModel))
|
|
|
+ }else{
|
|
|
+ complete(nil)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func creatImage(oldModel:TSActionInfoModel) {
|
|
|
+
|
|
|
+ initializeActionInfoModel(oldModel: oldModel)
|
|
|
+ if oldModel.upImageURLExpired { return }
|
|
|
+
|
|
|
+ generatingProgress = 0
|
|
|
+ stopNetwork = false
|
|
|
+ stateDatauPblished = (.start,nil)
|
|
|
+
|
|
|
+ currentActionInfoModel.status = "running"
|
|
|
+ currentActionInfoModel.actionStatus = .running
|
|
|
+ currentActionInfoModel.percent = 0
|
|
|
+ replaceSaveInfoModel(model: currentActionInfoModel)
|
|
|
+
|
|
|
+ stateDatauPblished = (.progressString(generating(progress: 0.0)),currentActionInfoModel)
|
|
|
+
|
|
|
+
|
|
|
+ let request = currentActionInfoModel.request
|
|
|
+
|
|
|
+
|
|
|
+ var urlType:TSNeURLType = .imageRewrite
|
|
|
+ var postDict:[String:Any] = [
|
|
|
+ "device":getUserInfoJsonString(),
|
|
|
+ "imageUrl":request.imageUrl,
|
|
|
+ "model":request.generatorStyle.aiModel
|
|
|
+ ]
|
|
|
+ switch request.generatorStyle {
|
|
|
+ case .enhance:
|
|
|
+ urlType = .imageRewrite
|
|
|
+ postDict["prompt"] = "把图片变清晰"
|
|
|
+ case .colorize:
|
|
|
+ urlType = .imageRewrite
|
|
|
+ postDict["prompt"] = "Add suitable colors to photos"
|
|
|
+ case .descratch:
|
|
|
+ urlType = .imageRewrite
|
|
|
+ postDict["prompt"] = "Remove the photo's scratches and dirt"
|
|
|
+ case .enlighten:
|
|
|
+ urlType = .imageRewrite
|
|
|
+ postDict["prompt"] = "Adjust the light and darkness of the photo to make the overall look coordinated"
|
|
|
+
|
|
|
+ case .recreate:
|
|
|
+ urlType = .imageRewrite
|
|
|
+ postDict["prompt"] = "Recreate damaged portraits and added suitable color for photo"
|
|
|
+ }
|
|
|
+
|
|
|
+ creatRequest = TSNetworkShared.post(urlType: urlType,parameters: postDict) { [weak self] data,error in
|
|
|
+ guard let self = self else { return }
|
|
|
+ if stopNetwork == true { return }
|
|
|
+
|
|
|
+ if let error = error {
|
|
|
+ handleFailInfoModel(errorString: error.tsDesc,code: error.tsCode)
|
|
|
+ }else{
|
|
|
+ if let dataDict = kNetWorkCodeSuccess(data: data),
|
|
|
+ let actionId = dataDict["actionId"] as? Int{
|
|
|
+ if stopNetwork == false {
|
|
|
+ self.stateDatauPblished = (.pending,nil) //通知首页进行更新
|
|
|
+ self.getActionInfo(action_id:actionId)
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ handleFailInfoModel(errorString: "",code: 0)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ override func generating(progress: Float) -> String {
|
|
|
+ let progress = Float(progress)*(0.9) // 预留 10% 进度给图片下载
|
|
|
+ //Generating 0%-100%
|
|
|
+ var progressInt = Int(progress*100)
|
|
|
+
|
|
|
+ if progressInt > 99 {
|
|
|
+ progressInt = 99
|
|
|
+ }
|
|
|
+
|
|
|
+ generatingProgress = progressInt
|
|
|
+ return "Generating".localized + " \(progressInt)%"
|
|
|
+ }
|
|
|
+
|
|
|
+ var imageMaxKb:Int{
|
|
|
+ return 10*1024
|
|
|
+ }
|
|
|
+
|
|
|
+ func uploadingPhoto(progress:Float) -> String {
|
|
|
+ //Uploading Photo 0%-100%
|
|
|
+ var progressInt = Int(progress*100)
|
|
|
+ if progressInt > 99 {
|
|
|
+ progressInt = 99
|
|
|
+ }
|
|
|
+ return "Uploading Photo".localized + " \(progressInt)%"
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ override func cancelCleanContent() {
|
|
|
+ super.cancelCleanContent()
|
|
|
+ debugPrint("cancelCleanContent")
|
|
|
+ uploadRequest?.cancel()
|
|
|
+ creatRequest?.cancel()
|
|
|
+ }
|
|
|
+}
|