123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- //
- // 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{
- 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
- model.request.generatorStyle = currentActionInfoModel.request.generatorStyle
- model.request.model = currentActionInfoModel.request.model
-
- if isSaveProcessToDB {
- saveDataDB()
- }
- currentActionInfoModel = model
- dePrint("model actionStatus 发出=\(model.actionStatus)")
- currentActionInfoModelChanged?(currentActionInfoModel)
- }
- override func saveDataDB() {
- if currentActionInfoModel.id == 0 {
- return
- }
- TSRMShared.aiListDB.updateData(currentActionInfoModel,uuid: uuid)
- }
- override func handleGenerateSuccess() {
- kPurchaseBusiness.useOnceForFree(type: currentActionInfoModel.request.generatorStyle.vipFreeNumType)
- saveDataDB()
- //生成成功后,不再提示用户上传规则弹窗
- UserDefaults.standard.set("1", forKey: currentActionInfoModel.request.generatorStyle.userDefaultsKey)
- UserDefaults.standard.synchronize()
-
- if isShowSuccessView {
- 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: "Process successfully".localized,deadline: 5.0) {
- let gennerateVC = TSAIListPhotoGeneratorVC(generatorModel: TSAIListPhotoGeneratorModel(upLoadImage: UIImage(), generatorStyle: cyModel.request.generatorStyle),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?
- var generateStyleModel:TSAIListPhotoGeneratorModel?
- 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.imageUrls = generateStyleModel.upLoadImageUrls
-
- infoModel.request.prompt = generateStyleModel.prompt
- infoModel.request.inputText = generateStyleModel.inputText
- infoModel.request.generatorStyle = generateStyleModel.generatorStyle
- infoModel.request.model = generateStyleModel.model
-
- infoModel.request.top = Float(generateStyleModel.expandEdge.top)
- infoModel.request.bottom = Float(generateStyleModel.expandEdge.bottom)
- infoModel.request.left = Float(generateStyleModel.expandEdge.left)
- infoModel.request.right = Float(generateStyleModel.expandEdge.right)
-
- return infoModel
- }
-
- func creatImage(oldModel:TSActionInfoModel,complete:@escaping (Bool)->Void) {
-
- 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)
-
- guard let getPostContent = getPostContent() else { return }
- creatRequest = TSNetworkShared.post(urlType: getPostContent.0,parameters: getPostContent.1) { [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)
- complete(false)
- }else{
- if let dataDict = kNetWorkCodeSuccess(data: data),
- let actionId = dataDict["actionId"] as? Int{
- if stopNetwork == false {
- complete(true)
- self.stateDatauPblished = (.pending,nil) //通知首页进行更新
- self.getActionInfo(action_id:actionId)
- }
- }else{
- handleFailInfoModel(errorString: "",code: 0)
- complete(false)
- }
- }
- }
- }
- 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 "Processing".localized + " " + kPercentlocalized(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 + " " + kPercentlocalized(progressInt)
- }
-
-
- override func cancelCleanContent() {
- super.cancelCleanContent()
- debugPrint("cancelCleanContent")
- uploadRequest?.cancel()
- creatRequest?.cancel()
- }
- }
- extension TSGenerateBasePhotoOperation {
-
- //上传一张图片
- func uploadImage(generateStyleModel:TSAIListPhotoGeneratorModel,complete:@escaping (TSActionInfoModel?)->Void) {
- self.generateStyleModel = generateStyleModel
- //走多张图片上传逻辑
- if let upLoadImages = generateStyleModel.upLoadImages ,upLoadImages.count > 0{
- uploadImages(generateStyleModel: generateStyleModel, complete: complete)
- return
- }
-
- 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
- TSImageStoreTool.storeImage(image: upLoadImage, urlString: string)
- complete(createActionInfoModel(generateStyleModel: generateStyleModel))
- }else{
- complete(nil)
- }
- }
- }
- }
-
- //上传多张图片
- func uploadImages(generateStyleModel:TSAIListPhotoGeneratorModel,complete:@escaping (TSActionInfoModel?)->Void) {
- guard let upLoadImages = generateStyleModel.upLoadImages else { return }
-
- var uploadImageUrls = [String?](repeating: nil, count: upLoadImages.count) // 预分配数组,保持原始顺序
- var errorOccurred = false
-
- let progressRatio:Float = 1.0/Float(upLoadImages.count)//进度分割比例
- var totalProgress:Float = 0.0//总进度
-
- stopNetwork = false
- stateDatauPblished = (.start,nil)
- stateDatauPblished = (.progressString(uploadingPhoto(progress: 0.0)),nil)
-
- let group = DispatchGroup()
- for (index, image) in upLoadImages.enumerated() {
- group.enter()
- uploadRequest = TSNetworkShared.uploadImage(upLoadImage: image, maxKb: generateStyleModel.generatorStyle.imageMaxKb) { [weak self] progress in
- guard let self = self else { return }
- totalProgress+=progress*progressRatio
- stateDatauPblished = (.progressString(uploadingPhoto(progress: totalProgress)),nil)
- } completion: { [weak self] data, error in
- guard let self = self else { return }
- if let error = error {
- errorOccurred = true
- self.stateDatauPblished = (TSProgressState.getFailed(error.tsDesc,error.tsCode),nil)
- }else{
- if let string = data as? String {
- // 按照原始索引位置存储结果
- uploadImageUrls[index] = string
- }
- }
- group.leave()
- }
- }
- group.notify(queue: .main) {
- if !errorOccurred && uploadImageUrls.compactMap({ $0 }).count == upLoadImages.count {
- // 所有图片上传成功,且顺序已保持
- let successfulUrls = uploadImageUrls.compactMap { $0 }
-
- generateStyleModel.upLoadImageUrl = successfulUrls.first
- generateStyleModel.upLoadImageUrls = successfulUrls
-
- // 存储图片到本地(顺序一致)
- for (index, url) in successfulUrls.enumerated() {
- TSImageStoreTool.storeImage(image: upLoadImages[index], urlString: url)
- }
- complete(self.createActionInfoModel(generateStyleModel: generateStyleModel))
- }else{
- self.stateDatauPblished = (TSProgressState.generalNormalFailed,nil)
- complete(nil)
- }
- }
- }
-
- }
- extension TSGenerateBasePhotoOperation {
- func getPostContent()->(TSNeURLType,[String:Any])?{
- let request = currentActionInfoModel.request
- var urlType:TSNeURLType = .imageRewrite
- var postDict:[String:Any] = [
- "device":getUserInfoJsonString(),
- "imageUrl":request.imageUrl,
- "model":request.model
- ]
-
- switch request.generatorStyle {
- case .creatVideo:
- urlType = .createVideo
- postDict["prompt"] = request.prompt
- postDict["duration"] = 5
- postDict["quality"] = "720p"
- case .remove:
- urlType = .imageInpaint
- postDict["maskUrl"] = request.imageUrls.last ?? request.imageUrl
- case .portraitFusion:
- guard request.imageUrls.count > 0 else { return nil}
- postDict["prompt"] = request.prompt
- postDict["imageUrls"] = request.imageUrls
- postDict.removeValue(forKey: "imageUrl")
- case .removeBg:
- urlType = .removeBg
- postDict["maskUrl"] = request.imageUrls.last ?? request.imageUrl
- case .photoExpand:
- urlType = .photoExpand
- guard let generatorModel = generateStyleModel else { return nil}
- postDict["prompt"] = request.prompt
- postDict["top"] = request.top
- postDict["left"] = request.left
- postDict["bottom"] = request.bottom
- postDict["right"] = request.right
- default:
- postDict["prompt"] = request.prompt
- break;
- }
- return (urlType,postDict)
- }
-
- }
|