TSGenerateBasePhotoOperation.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. //
  2. // TSGenerateBasePhotoOperation.swift
  3. // AIRingtone
  4. //
  5. // Created by 100Years on 2025/3/24.
  6. //
  7. import Combine
  8. import Alamofire
  9. import ObjectMapper
  10. import Kingfisher
  11. class TSGenerateBasePhotoOperationQueue: TSGenerateBaseOperationQueue {
  12. static let shared:TSGenerateBasePhotoOperationQueue = TSGenerateBasePhotoOperationQueue()
  13. func creatOperation(uuid: String) -> TSGenerateBasePhotoOperation {
  14. let operation = super.creatOperation(uuid: uuid, type: TSGenerateBasePhotoOperation.self)
  15. handleStateDatauPblished(uuid: uuid, generateOperation: operation as! TSGenerateBaseOperation, notificationName: .kGenerateBasePhotoOperation)
  16. return operation as! TSGenerateBasePhotoOperation
  17. }
  18. override func getUUIDData(uuid:String)->(TSProgressState,TSActionInfoModel?){
  19. if let PosterOperation = TSGenerateBasePhotoOperationQueue.shared.findOperation(uuid: uuid) as? TSGenerateBasePhotoOperation {
  20. dePrint("TSBaseOperation stateDatauPblished 发送 = \(PosterOperation.stateDatauPblished)")
  21. return (PosterOperation.stateDatauPblished.0,PosterOperation.currentActionInfoModel)
  22. }
  23. return (.none,TSActionInfoModel())
  24. }
  25. }
  26. class TSGenerateBasePhotoOperation: TSGenerateBaseOperation , @unchecked Sendable{
  27. override var actionInfoDict:[String:Any]{
  28. return [
  29. "actionType":"image_create",
  30. "comments": "Success",
  31. "costTime":9,
  32. "createdTimestamp":1742183242,
  33. "id":2449,
  34. "percent":1,
  35. "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\"}",
  36. "response": "{\"resultUrl\": \"https://be-aigc.s3-accelerate.amazonaws.com/4c946f78-b1e7-4ffe-ba18-fff26b10178c.png\"}",
  37. "status":"success"
  38. ]
  39. }
  40. override func replaceSaveInfoModel(model:TSActionInfoModel){
  41. model.uuid = uuid
  42. model.request.imageUrlTimestamp = currentActionInfoModel.request.imageUrlTimestamp
  43. model.request.generatorStyle = currentActionInfoModel.request.generatorStyle
  44. model.request.model = currentActionInfoModel.request.model
  45. if isSaveProcessToDB {
  46. saveDataDB()
  47. }
  48. currentActionInfoModel = model
  49. dePrint("model actionStatus 发出=\(model.actionStatus)")
  50. currentActionInfoModelChanged?(currentActionInfoModel)
  51. }
  52. override func saveDataDB() {
  53. if currentActionInfoModel.id == 0 {
  54. return
  55. }
  56. TSRMShared.aiListDB.updateData(currentActionInfoModel,uuid: uuid)
  57. }
  58. override func handleGenerateSuccess() {
  59. kPurchaseBusiness.useOnceForFree(type: currentActionInfoModel.request.generatorStyle.vipFreeNumType)
  60. saveDataDB()
  61. //生成成功后,不再提示用户上传规则弹窗
  62. UserDefaults.standard.set("1", forKey: currentActionInfoModel.request.generatorStyle.userDefaultsKey)
  63. UserDefaults.standard.synchronize()
  64. if isShowSuccessView {
  65. guard let window = WindowHelper.getKeyWindow() else {
  66. debugPrint("getKeyWindow nil")
  67. return
  68. }
  69. guard let rootVC = WindowHelper.topViewController() else {
  70. debugPrint("handleGenerateSuccess topViewController nil")
  71. return
  72. }
  73. let copyModel = self.currentActionInfoModel.copy()
  74. if let cyModel = copyModel as? TSActionInfoModel {
  75. let topY = k_Nav_Height+10
  76. debugPrint("topY=\(topY)")
  77. kSaveSuccesswShared.show(atView: window,text: "Process successfully".localized,deadline: 5.0) {
  78. let gennerateVC = TSAIListPhotoGeneratorVC(generatorModel: TSAIListPhotoGeneratorModel(upLoadImage: UIImage(), generatorStyle: cyModel.request.generatorStyle),infoModel: cyModel) { model in }
  79. gennerateVC.modalPresentationStyle = .overFullScreen
  80. gennerateVC.modalTransitionStyle = .crossDissolve
  81. rootVC.present(gennerateVC, animated: true)
  82. }
  83. }else{
  84. debugPrint("copyModel as? TSActionInfoModel error")
  85. }
  86. }
  87. }
  88. /**
  89. 1.用户上传图片
  90. 2.调用生成接口
  91. 3.后台返回查询 id,根据 id 不断查询进度和结果.(此时才允许进度后台)
  92. 利用3后台返回的TSActionRequestModel中存着关键的请求接口数据,请求数据
  93. */
  94. private var uploadRequest:Request?
  95. private var creatRequest:Request?
  96. var generateStyleModel:TSAIListPhotoGeneratorModel?
  97. func createActionInfoModel(generateStyleModel:TSAIListPhotoGeneratorModel) -> TSActionInfoModel? {
  98. guard let upLoadImageUrl = generateStyleModel.upLoadImageUrl else { return nil }
  99. let infoModel = TSActionInfoModel()
  100. infoModel.id = Date.timestampInt
  101. infoModel.request = TSActionRequestModel()
  102. infoModel.request.imageUrl = upLoadImageUrl
  103. infoModel.request.imageUrlTimestamp = Date.timestampInt
  104. infoModel.request.imageUrls = generateStyleModel.upLoadImageUrls
  105. infoModel.request.prompt = generateStyleModel.prompt
  106. infoModel.request.inputText = generateStyleModel.inputText
  107. infoModel.request.generatorStyle = generateStyleModel.generatorStyle
  108. infoModel.request.model = generateStyleModel.model
  109. infoModel.request.top = Float(generateStyleModel.expandEdge.top)
  110. infoModel.request.bottom = Float(generateStyleModel.expandEdge.bottom)
  111. infoModel.request.left = Float(generateStyleModel.expandEdge.left)
  112. infoModel.request.right = Float(generateStyleModel.expandEdge.right)
  113. return infoModel
  114. }
  115. func creatImage(oldModel:TSActionInfoModel,complete:@escaping (Bool)->Void) {
  116. initializeActionInfoModel(oldModel: oldModel)
  117. if oldModel.upImageURLExpired { return }
  118. generatingProgress = 0
  119. stopNetwork = false
  120. stateDatauPblished = (.start,nil)
  121. currentActionInfoModel.status = "running"
  122. currentActionInfoModel.actionStatus = .running
  123. currentActionInfoModel.percent = 0
  124. replaceSaveInfoModel(model: currentActionInfoModel)
  125. stateDatauPblished = (.progressString(generating(progress: 0.0)),currentActionInfoModel)
  126. guard let getPostContent = getPostContent() else { return }
  127. creatRequest = TSNetworkShared.post(urlType: getPostContent.0,parameters: getPostContent.1) { [weak self] data,error in
  128. guard let self = self else { return }
  129. if stopNetwork == true { return }
  130. if let error = error {
  131. handleFailInfoModel(errorString: error.tsDesc,code: error.tsCode)
  132. complete(false)
  133. }else{
  134. if let dataDict = kNetWorkCodeSuccess(data: data),
  135. let actionId = dataDict["actionId"] as? Int{
  136. if stopNetwork == false {
  137. complete(true)
  138. self.stateDatauPblished = (.pending,nil) //通知首页进行更新
  139. self.getActionInfo(action_id:actionId)
  140. }
  141. }else{
  142. handleFailInfoModel(errorString: "",code: 0)
  143. complete(false)
  144. }
  145. }
  146. }
  147. }
  148. override func generating(progress: Float) -> String {
  149. let progress = Float(progress)*(0.9) // 预留 10% 进度给图片下载
  150. //Generating 0%-100%
  151. var progressInt = Int(progress*100)
  152. if progressInt > 99 {
  153. progressInt = 99
  154. }
  155. generatingProgress = progressInt
  156. return "Processing".localized + " " + kPercentlocalized(progressInt)
  157. }
  158. var imageMaxKb:Int{
  159. return 10*1024
  160. }
  161. func uploadingPhoto(progress:Float) -> String {
  162. //Uploading Photo 0%-100%
  163. var progressInt = Int(progress*100)
  164. if progressInt > 99 {
  165. progressInt = 99
  166. }
  167. return "Uploading Photo".localized + " " + kPercentlocalized(progressInt)
  168. }
  169. override func cancelCleanContent() {
  170. super.cancelCleanContent()
  171. debugPrint("cancelCleanContent")
  172. uploadRequest?.cancel()
  173. creatRequest?.cancel()
  174. }
  175. }
  176. extension TSGenerateBasePhotoOperation {
  177. //上传一张图片
  178. func uploadImage(generateStyleModel:TSAIListPhotoGeneratorModel,complete:@escaping (TSActionInfoModel?)->Void) {
  179. self.generateStyleModel = generateStyleModel
  180. //走多张图片上传逻辑
  181. if let upLoadImages = generateStyleModel.upLoadImages ,upLoadImages.count > 0{
  182. uploadImages(generateStyleModel: generateStyleModel, complete: complete)
  183. return
  184. }
  185. let upLoadImage = generateStyleModel.upLoadImage
  186. if let imageUrl = generateStyleModel.upLoadImageUrl,imageUrl.contains("http") {
  187. complete(createActionInfoModel(generateStyleModel: generateStyleModel))
  188. return
  189. }
  190. stopNetwork = false
  191. stateDatauPblished = (.start,nil)
  192. stateDatauPblished = (.progressString(uploadingPhoto(progress: 0.0)),currentActionInfoModel)
  193. uploadRequest = TSNetworkShared.uploadImage(upLoadImage: upLoadImage, maxKb: imageMaxKb) { [weak self] progress in
  194. guard let self = self else { return }
  195. if generatingProgress == 0 {
  196. stateDatauPblished = (.progressString(uploadingPhoto(progress: progress)),currentActionInfoModel)
  197. }
  198. } completion: { [weak self] data, error in
  199. guard let self = self else { return }
  200. if stopNetwork == true { return }
  201. if let error = error {
  202. generateStyleModel.upLoadImageUrl = nil
  203. self.stateDatauPblished = (TSProgressState.getFailed(error.tsDesc,error.tsCode),nil)
  204. complete(nil)
  205. }else{
  206. if let string = data as? String {
  207. generateStyleModel.upLoadImageUrl = string
  208. TSImageStoreTool.storeImage(image: upLoadImage, urlString: string)
  209. complete(createActionInfoModel(generateStyleModel: generateStyleModel))
  210. }else{
  211. complete(nil)
  212. }
  213. }
  214. }
  215. }
  216. //上传多张图片
  217. func uploadImages(generateStyleModel:TSAIListPhotoGeneratorModel,complete:@escaping (TSActionInfoModel?)->Void) {
  218. guard let upLoadImages = generateStyleModel.upLoadImages else { return }
  219. var uploadImageUrls = [String?](repeating: nil, count: upLoadImages.count) // 预分配数组,保持原始顺序
  220. var errorOccurred = false
  221. let progressRatio:Float = 1.0/Float(upLoadImages.count)//进度分割比例
  222. var totalProgress:Float = 0.0//总进度
  223. stopNetwork = false
  224. stateDatauPblished = (.start,nil)
  225. stateDatauPblished = (.progressString(uploadingPhoto(progress: 0.0)),nil)
  226. let group = DispatchGroup()
  227. for (index, image) in upLoadImages.enumerated() {
  228. group.enter()
  229. uploadRequest = TSNetworkShared.uploadImage(upLoadImage: image, maxKb: generateStyleModel.generatorStyle.imageMaxKb) { [weak self] progress in
  230. guard let self = self else { return }
  231. totalProgress+=progress*progressRatio
  232. stateDatauPblished = (.progressString(uploadingPhoto(progress: totalProgress)),nil)
  233. } completion: { [weak self] data, error in
  234. guard let self = self else { return }
  235. if let error = error {
  236. errorOccurred = true
  237. self.stateDatauPblished = (TSProgressState.getFailed(error.tsDesc,error.tsCode),nil)
  238. }else{
  239. if let string = data as? String {
  240. // 按照原始索引位置存储结果
  241. uploadImageUrls[index] = string
  242. }
  243. }
  244. group.leave()
  245. }
  246. }
  247. group.notify(queue: .main) {
  248. if !errorOccurred && uploadImageUrls.compactMap({ $0 }).count == upLoadImages.count {
  249. // 所有图片上传成功,且顺序已保持
  250. let successfulUrls = uploadImageUrls.compactMap { $0 }
  251. generateStyleModel.upLoadImageUrl = successfulUrls.first
  252. generateStyleModel.upLoadImageUrls = successfulUrls
  253. // 存储图片到本地(顺序一致)
  254. for (index, url) in successfulUrls.enumerated() {
  255. TSImageStoreTool.storeImage(image: upLoadImages[index], urlString: url)
  256. }
  257. complete(self.createActionInfoModel(generateStyleModel: generateStyleModel))
  258. }else{
  259. self.stateDatauPblished = (TSProgressState.generalNormalFailed,nil)
  260. complete(nil)
  261. }
  262. }
  263. }
  264. }
  265. extension TSGenerateBasePhotoOperation {
  266. func getPostContent()->(TSNeURLType,[String:Any])?{
  267. let request = currentActionInfoModel.request
  268. var urlType:TSNeURLType = .imageRewrite
  269. var postDict:[String:Any] = [
  270. "device":getUserInfoJsonString(),
  271. "imageUrl":request.imageUrl,
  272. "model":request.model
  273. ]
  274. switch request.generatorStyle {
  275. case .creatVideo:
  276. urlType = .createVideo
  277. postDict["prompt"] = request.prompt
  278. postDict["duration"] = 5
  279. postDict["quality"] = "720p"
  280. case .remove:
  281. urlType = .imageInpaint
  282. postDict["maskUrl"] = request.imageUrls.last ?? request.imageUrl
  283. case .portraitFusion:
  284. guard request.imageUrls.count > 0 else { return nil}
  285. postDict["prompt"] = request.prompt
  286. postDict["imageUrls"] = request.imageUrls
  287. postDict.removeValue(forKey: "imageUrl")
  288. case .removeBg:
  289. urlType = .removeBg
  290. postDict["maskUrl"] = request.imageUrls.last ?? request.imageUrl
  291. case .photoExpand:
  292. urlType = .photoExpand
  293. guard let generatorModel = generateStyleModel else { return nil}
  294. postDict["prompt"] = request.prompt
  295. postDict["top"] = request.top
  296. postDict["left"] = request.left
  297. postDict["bottom"] = request.bottom
  298. postDict["right"] = request.right
  299. default:
  300. postDict["prompt"] = request.prompt
  301. break;
  302. }
  303. return (urlType,postDict)
  304. }
  305. }