PQStuckPointPublicController.swift 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552
  1. //
  2. // PQStuckPointPublicController.swift
  3. // PQSpeed
  4. //
  5. // Created by SanW on 2021/5/6.
  6. // Copyright © 2021 BytesFlow. All rights reserved.
  7. //
  8. import ObjectMapper
  9. import Photos
  10. import UIKit
  11. import WechatOpenSDK
  12. class PQStuckPointPublicController: PQBaseViewController {
  13. private var isShared: Bool = false // 是否在分享
  14. private var isExportSuccess: Bool = false // 是否导出完成
  15. private var isSaveDraftSuccess: Bool = false // 是否保存草稿完成
  16. private var isSaveProjectSuccess: Bool = false // 是否保存项目完成
  17. private var isUploadSuccess: Bool = false // 是否上传完成
  18. private var isPublicSuccess: Bool = false // 是否发布完成
  19. private var exportLocalURL: URL? // 导出的地址
  20. // 再创作数据
  21. private var reCreateData: PQReCreateModel?
  22. // 确定上传的数据
  23. private var uploadData: PQUploadModel?
  24. // 发布成功的视频数据
  25. private var videoData: PQVideoListModel?
  26. // 视频创作埋点数据
  27. private var eventTrackData: PQVideoMakeEventTrackModel?
  28. // 选中的总时长-统计使用
  29. var selectedTotalDuration: Float64 = 0
  30. // 选择的总数-统计使用
  31. var selectedDataCount: Int = 0
  32. // 选择的图片总数-统计使用
  33. var selectedImageDataCount: Int = 0
  34. // 最大的宽度
  35. private var maxWidth: CGFloat = cScreenWidth
  36. // 最大的高度
  37. private var maxHeight: CGFloat = 385
  38. // 开始导出的时间
  39. private let startExportDate: Float64 = Date().timeIntervalSince1970
  40. // 导出结束的时间
  41. private var exportEndDate: Float64 = Date().timeIntervalSince1970
  42. // 取到的封面 给发布界面使用
  43. private var coverImage: UIImage?
  44. // 导出视频工具类
  45. private var exporter: PQCompositionExporter!
  46. // 导出进度
  47. private var exportProgrss = 0
  48. var mStickers: [PQEditVisionTrackMaterialsModel]?
  49. var remindView: PQRemindView?
  50. //已经选择标题内容,加一个属性接收 使用有不在主线不能直接使用 titleLabel text
  51. var selectTitle:String = ""
  52. //add by ak 玩法类型 调用 producevideo/saveProject 时使用
  53. var rhythmMode:createStickersModel = .createStickersModelPoint
  54. //add by ak 设置的速度
  55. var syncedUpVideoSpeedMax:Float = 0.0
  56. var syncedUpVideoSpeedMin:Float = 0.0
  57. //add by ak 是否是再创作模式
  58. var isReCreate:Bool = false
  59. //最终使用的音频时长,用于拼接音乐使用
  60. var finallyUserAudioTime:Float = 0.0
  61. //拼接音乐的开始和结束位置
  62. var clipAudioRange: CMTimeRange = CMTimeRange.zero
  63. // 导出的开始的开始和结束时间
  64. var playeTimeRange: CMTimeRange = CMTimeRange()
  65. // 预览大小
  66. private var preViewSize: CGSize {
  67. switch aspectRatio {
  68. case let .origin(width, height):
  69. var tempHeight: CGFloat = 0
  70. var tempWidth: CGFloat = 0
  71. if width > height {
  72. tempWidth = maxWidth
  73. tempHeight = (maxWidth * height / width)
  74. if tempHeight > maxHeight {
  75. tempHeight = maxHeight
  76. tempWidth = (maxHeight * width / height)
  77. }
  78. } else {
  79. tempHeight = maxHeight
  80. tempWidth = (maxHeight * width / height)
  81. if tempWidth > maxWidth {
  82. tempWidth = maxWidth
  83. tempHeight = (maxWidth * height / width)
  84. }
  85. }
  86. if tempHeight.isNaN || tempWidth.isNaN {
  87. return CGSize.zero
  88. } else {
  89. return CGSize(width: tempWidth, height: tempHeight)
  90. }
  91. case .oneToOne:
  92. if maxWidth > maxHeight {
  93. return CGSize(width: maxHeight, height: maxHeight)
  94. } else {
  95. return CGSize(width: maxWidth, height: maxWidth)
  96. }
  97. case .sixteenToNine:
  98. return CGSize(width: maxWidth, height: maxWidth * 9.0 / 16.0)
  99. case .nineToSixteen:
  100. return CGSize(width: maxHeight * 9.0 / 16.0, height: maxHeight)
  101. default:
  102. break
  103. }
  104. return CGSize(width: maxHeight, height: maxHeight)
  105. }
  106. // 背景音乐
  107. var audioMixModel: PQVoiceModel?
  108. // 画面比例
  109. var aspectRatio: aspectRatio?
  110. // 导出的项目数据
  111. var editProjectModel: PQEditProjectModel? {
  112. didSet {
  113. aspectRatio = PQPlayerViewModel.videoCanvasTypeToAspectRatio(projectModel: editProjectModel)
  114. var totalDuration: Float64 = 0
  115. if editProjectModel?.sData?.sections.count ?? 0 > 0 {
  116. for section in (editProjectModel?.sData?.sections)! {
  117. totalDuration = totalDuration + section.sectionDuration
  118. }
  119. }
  120. editProjectModel?.sData?.videoMetaData?.duration = totalDuration
  121. if editProjectModel?.sData?.sections != nil, (editProjectModel?.sData?.sections.count ?? 0) > 0 {
  122. // 查找出背景图并设置
  123. var coverImageMaterialsModel: PQEditVisionTrackMaterialsModel?
  124. for section in (editProjectModel?.sData?.sections)! {
  125. if coverImageMaterialsModel != nil {
  126. break
  127. }
  128. coverImageMaterialsModel = section.sectionTimeline?.visionTrack?.getEnableVisionTrackMaterials().first
  129. }
  130. if coverImageMaterialsModel != nil {
  131. coverImage = coverImageMaterialsModel?.getCoverImage()
  132. playerHeaderView.image = coverImage
  133. playerHeaderView.contentMode = coverImageMaterialsModel!.canvasFillType == stickerContentMode.aspectFitStr.rawValue ? .scaleAspectFill : .scaleAspectFit
  134. }
  135. }
  136. }
  137. }
  138. /// 所有需要导出的filter
  139. var filters: Array = Array<ImageProcessingOperation>.init()
  140. /// 预览背景页
  141. lazy var bgTopView: UIView = {
  142. let bgTopView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: maxHeight))
  143. bgTopView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
  144. return bgTopView
  145. }()
  146. // 预览界面
  147. var playerHeaderView: UIImageView = {
  148. let playerHeaderView = UIImageView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: 0))
  149. playerHeaderView.isUserInteractionEnabled = true
  150. playerHeaderView.contentMode = .scaleAspectFit
  151. playerHeaderView.clipsToBounds = true
  152. return playerHeaderView
  153. }()
  154. // add by ak 播放器的封面 为了不和原有的播放器层级单独添加一个 view
  155. lazy var playerHeaderCoverImageView: UIImageView = {
  156. let playerHeaderCoverImageView = UIImageView.init()
  157. playerHeaderCoverImageView.isUserInteractionEnabled = true
  158. playerHeaderCoverImageView.contentMode = .scaleAspectFit
  159. playerHeaderCoverImageView.clipsToBounds = true
  160. let playBtn = UIButton(type: .custom)
  161. playBtn.setImage(UIImage().BF_Image(named: "icon_video_play"), for: .normal)
  162. playBtn.tag = 4
  163. playBtn.isUserInteractionEnabled = false
  164. playerHeaderCoverImageView.addSubview(playBtn)
  165. playerHeaderCoverImageView.isHidden = true
  166. return playerHeaderCoverImageView
  167. }()
  168. /// 播放器
  169. lazy var avPlayer: AVPlayer = {
  170. let avPlayer = AVPlayer()
  171. NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avPlayer.currentItem, queue: .main) { [weak self] notify in
  172. BFLog(message: "AVPlayerItemDidPlayToEndTime = \(notify)")
  173. avPlayer.seek(to: CMTime.zero)
  174. if(self?.playerHeaderCoverImageView.image != nil){
  175. self?.playerHeaderCoverImageView.isHidden = false
  176. }
  177. self?.playBtn.isHidden = false
  178. }
  179. NotificationCenter.default.addObserver(forName: .AVPlayerItemNewErrorLogEntry, object: avPlayer.currentItem, queue: .main) { notify in
  180. BFLog(message: "AVPlayerItemNewErrorLogEntry = \(notify)")
  181. }
  182. NotificationCenter.default.addObserver(forName: .AVPlayerItemFailedToPlayToEndTime, object: avPlayer.currentItem, queue: .main) { notify in
  183. BFLog(message: "AVPlayerItemFailedToPlayToEndTime = \(notify)")
  184. }
  185. NotificationCenter.default.addObserver(forName: .AVPlayerItemPlaybackStalled, object: avPlayer.currentItem, queue: .main) { notify in
  186. BFLog(message: "AVPlayerItemPlaybackStalled = \(notify)")
  187. }
  188. avPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 1000), queue: .main) { [weak self] _ in
  189. let progress = CMTimeGetSeconds(avPlayer.currentItem?.currentTime() ?? CMTime.zero) / CMTimeGetSeconds(avPlayer.currentItem?.duration ?? CMTime.zero)
  190. if progress >= 1 {
  191. self?.playBtn.isHidden = false
  192. }
  193. }
  194. return avPlayer
  195. }()
  196. /// 预览layer
  197. lazy var playerLayer: AVPlayerLayer = {
  198. let playerLayer = AVPlayerLayer(player: avPlayer)
  199. playerLayer.frame = playerHeaderView.bounds
  200. return playerLayer
  201. }()
  202. /// 播放按钮
  203. lazy var playBtn: UIButton = {
  204. let playBtn = UIButton(type: .custom)
  205. playBtn.frame = CGRect(x: (preViewSize.width - cDefaultMargin * 5) / 2, y: (preViewSize.height - cDefaultMargin * 5) / 2, width: cDefaultMargin * 5, height: cDefaultMargin * 5)
  206. playBtn.setImage(UIImage().BF_Image(named: "icon_video_play"), for: .normal)
  207. playBtn.tag = 4
  208. playBtn.isHidden = true
  209. playBtn.isUserInteractionEnabled = false
  210. return playBtn
  211. }()
  212. // progressTipsLab
  213. lazy var progressTipsLab: UILabel = {
  214. let progressTipsLab = UILabel()
  215. progressTipsLab.textAlignment = .center
  216. progressTipsLab.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  217. progressTipsLab.numberOfLines = 2
  218. progressTipsLab.textColor = UIColor.white
  219. let attributedText = NSMutableAttributedString(string: "0%\n视频正在处理中,请勿离开")
  220. attributedText.addAttributes([.font: UIFont.systemFont(ofSize: 34)], range: NSRange(location: 0, length: 2))
  221. progressTipsLab.attributedText = attributedText
  222. progressTipsLab.addShadow()
  223. return progressTipsLab
  224. }()
  225. // 进度条
  226. lazy var progressView: UIProgressView = {
  227. let progressView = UIProgressView(progressViewStyle: .default)
  228. progressView.trackTintColor = UIColor(white: 0, alpha: 0.5)
  229. progressView.progressTintColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
  230. progressView.transform = CGAffineTransform(scaleX: 1.0, y: playerHeaderView.frame.height)
  231. return progressView
  232. }()
  233. //提示
  234. lazy var remindLab: UILabel = {
  235. let remindLab = UILabel()
  236. remindLab.font = UIFont.boldSystemFont(ofSize: 18)
  237. remindLab.textColor = PQBFConfig.shared.styleTitleColor
  238. remindLab.textAlignment = .center
  239. remindLab.numberOfLines = 2
  240. remindLab.backgroundColor = .clear
  241. remindLab.text = "为你的大作起个响亮的标题\n分享秀一下🎉"
  242. return remindLab
  243. }()
  244. //输入框背景
  245. lazy var inputBackView: UIView = {
  246. let inputBackView = UIView()
  247. inputBackView.backgroundColor = .clear
  248. inputBackView.layer.cornerRadius = 7
  249. inputBackView.layer.borderWidth = 2
  250. inputBackView.layer.borderColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue).cgColor
  251. return inputBackView
  252. }()
  253. // 手势提示
  254. lazy var pinView: UIImageView = {
  255. let pinView = UIImageView()
  256. pinView.kf.setImage(with: URL(fileURLWithPath: Bundle().BF_mainbundle().path(forResource: "editCoverPin", ofType: ".gif")!))
  257. return pinView
  258. }()
  259. //封面
  260. lazy var coverImageView: UIImageView = {
  261. let coverImageView = UIImageView()
  262. coverImageView.isUserInteractionEnabled = true
  263. coverImageView.backgroundColor = .clear
  264. coverImageView.contentMode = .scaleToFill
  265. return coverImageView
  266. }()
  267. //封面标题
  268. lazy var coverImageTitle: UILabel = {
  269. let coverImageTitle = UILabel()
  270. coverImageTitle.text = "换封面"
  271. coverImageTitle.textAlignment = .center
  272. coverImageTitle.backgroundColor = UIColor(red: 0.22, green: 0.26, blue: 0.35, alpha: 0.5)
  273. coverImageTitle.isUserInteractionEnabled = true
  274. coverImageTitle.textColor = .white
  275. coverImageTitle.font = UIFont.boldSystemFont(ofSize: 12)
  276. return coverImageTitle
  277. }()
  278. //标题
  279. lazy var titleLabel: UILabel = {
  280. let titleLabel = UILabel()
  281. titleLabel.numberOfLines = 2
  282. titleLabel.isUserInteractionEnabled = true
  283. titleLabel.textColor = UIColor.hexColor(hexadecimal: "#ABABAB")
  284. titleLabel.textAlignment = .left
  285. titleLabel.font = UIFont.systemFont(ofSize: 17)
  286. let ges = UITapGestureRecognizer(target: self, action: #selector(titleLabelClick))
  287. titleLabel.addGestureRecognizer(ges)
  288. return titleLabel
  289. }()
  290. //编辑发布标题
  291. lazy var publicTitleView: PQEditPublicTitleView = {
  292. let publicTitleView = PQEditPublicTitleView()
  293. publicTitleView.isHidden = true
  294. publicTitleView.confirmBtnClock = { [weak self] title in
  295. BFLog(message: "传出的 title is :\(String(describing: title))")
  296. if(title?.count != 0 && title != self?.titleLabel.text){
  297. self?.changPlayerIsPause(isPause: false)
  298. //判断文字是否有效
  299. var inputText = ""
  300. inputText = title?.replacingOccurrences(of: "\n", with: "") ?? ""
  301. inputText = inputText.replacingOccurrences(of: " ", with: "")
  302. if(inputText.count > 0){
  303. self?.setTitleText(text: title ?? "",textColor: .black)
  304. //更新数据
  305. self?.videoData?.title = title
  306. self?.updateCoverImagegOrTitle()
  307. }
  308. }
  309. }
  310. publicTitleView.viewIsHiddenCallBack = { [weak self] in
  311. self?.changPlayerIsPause(isPause: false)
  312. }
  313. return publicTitleView
  314. }()
  315. //编辑发布封面
  316. lazy var publicEditCoverView: PQEditPublicCoverImageView = {
  317. let publicEditCoverView = PQEditPublicCoverImageView.init(frame: CGRect.init(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
  318. publicEditCoverView.isHidden = true
  319. return publicEditCoverView
  320. }()
  321. //分享到朋友圈
  322. lazy var shareWechatBtn: UIButton = {
  323. let shareWechatBtn = UIButton(type: .custom)
  324. shareWechatBtn.frame = CGRect(x: 0, y: 0, width: 70, height: 70)
  325. shareWechatBtn.setImage(UIImage().BF_Image(named: "reCreate_opration_wechat"), for: .normal)
  326. shareWechatBtn.backgroundColor = PQBFConfig.shared.styleBackGroundColor
  327. shareWechatBtn.addCorner(corner: 6)
  328. shareWechatBtn.tag = 2
  329. shareWechatBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  330. return shareWechatBtn
  331. }()
  332. //分享到好友
  333. lazy var shareFriendBtn: UIButton = {
  334. let shareFriendBtn = UIButton(type: .custom)
  335. shareFriendBtn.frame = CGRect(x: 0, y: 0, width: 70, height: 70)
  336. shareFriendBtn.setImage(UIImage().BF_Image(named: "reCreate_opration_friend"), for: .normal)
  337. shareFriendBtn.backgroundColor = PQBFConfig.shared.styleBackGroundColor
  338. shareFriendBtn.addCorner(corner: 6)
  339. shareFriendBtn.tag = 1
  340. shareFriendBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  341. return shareFriendBtn
  342. }()
  343. //关闭
  344. lazy var finishedBtn: UIButton = {
  345. let finishedBtn = UIButton(type: .custom)
  346. finishedBtn.setTitle("完成", for: .normal)
  347. finishedBtn.setTitleColor(UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue), for: .normal)
  348. finishedBtn.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
  349. finishedBtn.backgroundColor = .clear
  350. finishedBtn.tag = 3
  351. finishedBtn.addCorner(corner: 3)
  352. finishedBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  353. return finishedBtn
  354. }()
  355. /// 背景View
  356. lazy var oprationBgView: UIView = {
  357. let oprationBgView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: view.frame.height - cDevice_iPhoneNavBarAndStatusBarHei))
  358. oprationBgView.backgroundColor = .clear
  359. return oprationBgView
  360. }()
  361. //除了播放器以外的 下半部分操作区
  362. lazy var bottomOprationBgView: UIView = {
  363. let bottomOprationBgView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei + maxHeight, width: cScreenWidth, height: view.frame.height - cDevice_iPhoneNavBarAndStatusBarHei - maxHeight))
  364. bottomOprationBgView.backgroundColor = .clear
  365. bottomOprationBgView.isHidden = true
  366. return bottomOprationBgView
  367. }()
  368. override func backBtnClick() {
  369. if isExportSuccess {
  370. navigationController?.popViewController(animated: true)
  371. } else {
  372. view.endEditing(true)
  373. let remindData = PQBaseModel()
  374. remindData.title = "编辑的内容,将不会被保存"
  375. remindView = PQRemindView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
  376. remindView?.isBanned = true
  377. remindView?.confirmBtn.setTitle("确认", for: .normal)
  378. remindView?.cancelBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#333333"), for: .normal)
  379. remindView?.confirmBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#EE0051"), for: .normal)
  380. UIApplication.shared.keyWindow?.addSubview(remindView!)
  381. remindView?.remindData = remindData
  382. remindView?.remindBlock = { [weak self] item, _ in
  383. if item.tag == 2 {
  384. // 取消导出
  385. if self?.exporter != nil {
  386. self?.exporter.cancel()
  387. }
  388. self?.navigationController?.popViewController(animated: true)
  389. }
  390. }
  391. }
  392. }
  393. override func viewDidLoad() {
  394. super.viewDidLoad()
  395. // 注册上传成功的通知
  396. addNotification(self, selector: #selector(uploadSuccess(notify:)), name: cUploadSuccessKey, object: nil)
  397. PQNotification.addObserver(self, selector: #selector(didBecomeActiveNotification), name: UIApplication.didBecomeActiveNotification, object: nil)
  398. leftButton(image: "icon_detail_back", tintColor: PQBFConfig.shared.styleTitleColor)
  399. navHeadImageView?.backgroundColor = UIColor.clear
  400. lineView?.removeFromSuperview()
  401. view.addSubview(bgTopView)
  402. playerHeaderView.frame = CGRect(origin: CGPoint(x: (cScreenWidth - preViewSize.width) / 2, y: (maxHeight - preViewSize.height) / 2), size: preViewSize)
  403. let ges = UITapGestureRecognizer(target: self, action: #selector(playVideo))
  404. playerHeaderView.addGestureRecognizer(ges)
  405. if playerLayer.superlayer == nil {
  406. playerHeaderView.layer.insertSublayer(playerLayer, at: 0)
  407. }
  408. playerHeaderView.addSubview(playBtn)
  409. playerHeaderView.addSubview(progressView)
  410. view.addSubview(oprationBgView)
  411. oprationBgView.addSubview(progressTipsLab)
  412. // 添加导出view
  413. bgTopView.addSubview(playerHeaderView)
  414. playerHeaderCoverImageView.frame = playerHeaderView.frame
  415. playerHeaderCoverImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(playVideo)))
  416. (playerHeaderCoverImageView.viewWithTag(4))?.frame =
  417. CGRect(x: (preViewSize.width - cDefaultMargin * 5) / 2, y: (preViewSize.height - cDefaultMargin * 5) / 2, width: cDefaultMargin * 5, height: cDefaultMargin * 5)
  418. bgTopView.addSubview(playerHeaderCoverImageView)
  419. view.addSubview(bottomOprationBgView)
  420. bottomOprationBgView.addSubview(remindLab)
  421. bottomOprationBgView.addSubview(shareWechatBtn)
  422. bottomOprationBgView.addSubview(shareFriendBtn)
  423. bottomOprationBgView.addSubview(finishedBtn)
  424. bottomOprationBgView.addSubview(inputBackView)
  425. bottomOprationBgView.addSubview(pinView)
  426. inputBackView.addSubview(coverImageView)
  427. coverImageView.addSubview(coverImageTitle)
  428. inputBackView.addSubview(titleLabel)
  429. view.addSubview(publicTitleView)
  430. view.addSubview(publicEditCoverView)
  431. coverImageTitle.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(settingCoverImage)))
  432. coverImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(settingCoverImage)))
  433. progressView.snp.makeConstraints { make in
  434. make.left.right.centerY.equalTo(playerHeaderView)
  435. make.height.equalTo(3)
  436. }
  437. progressTipsLab.snp.makeConstraints { make in
  438. make.centerX.equalToSuperview()
  439. make.top.equalToSuperview().offset(((preViewSize.height - 90) / 2) + ((maxHeight - preViewSize.height) / 2))
  440. make.width.equalToSuperview()
  441. make.height.equalTo(90)
  442. }
  443. finishedBtn.snp.makeConstraints { make in
  444. make.centerX.equalToSuperview()
  445. make.bottom.equalToSuperview().offset(-cSafeAreaHeight)
  446. make.width.equalTo(100)
  447. make.height.equalTo(22)
  448. }
  449. shareWechatBtn.snp.makeConstraints { make in
  450. make.right.equalTo(view.snp_centerX).offset(-cDefaultMargin)
  451. make.width.equalTo(164)
  452. make.height.equalTo(52)
  453. make.bottom.equalTo(finishedBtn.snp_top).offset(-32)
  454. }
  455. shareFriendBtn.snp.makeConstraints { make in
  456. make.left.equalTo(view.snp_centerX).offset(cDefaultMargin)
  457. make.width.bottom.height.equalTo(shareWechatBtn)
  458. }
  459. inputBackView.snp.makeConstraints { make in
  460. make.centerX.equalToSuperview()
  461. make.bottom.equalTo(shareWechatBtn.snp_top).offset(-16)
  462. make.width.equalTo(343)
  463. make.height.equalTo(109)
  464. }
  465. //根据横竖屏设置不同的 UI
  466. let isWidth:Bool = (Float(editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) / Float(editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) ) >= 1
  467. var coverImageViewHeight = 50.0 * Float(editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) / Float(editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)
  468. if(coverImageViewHeight > 89){
  469. coverImageViewHeight = 89
  470. }
  471. coverImageView.snp.makeConstraints { make in
  472. make.left.equalToSuperview().offset(12)
  473. make.width.equalTo(50)
  474. make.top.equalToSuperview().offset(10)
  475. make.height.equalTo(coverImageViewHeight)
  476. }
  477. coverImageTitle.snp.makeConstraints { make in
  478. make.left.equalToSuperview()
  479. make.width.equalTo(50)
  480. make.top.equalTo(coverImageView.snp_bottom).offset(isWidth ? 0 : -23)
  481. make.height.equalTo(23)
  482. }
  483. remindLab.snp.makeConstraints { make in
  484. make.centerX.equalToSuperview()
  485. make.bottom.equalTo(inputBackView.snp_top).offset(-16).priorityHigh()
  486. make.height.equalTo(44)
  487. make.top.greaterThanOrEqualTo(5)
  488. make.bottom.lessThanOrEqualTo(inputBackView.snp_top).offset(-5)
  489. }
  490. titleLabel.snp.makeConstraints { make in
  491. make.height.equalTo(48)
  492. make.left.equalTo(coverImageView.snp_right).offset(12)
  493. make.right.equalToSuperview().offset(-14)
  494. make.top.equalToSuperview().offset(10)
  495. }
  496. pinView.snp.makeConstraints { make in
  497. make.height.width.equalTo(72)
  498. make.right.equalToSuperview()
  499. make.bottom.equalTo(inputBackView.snp_bottom)
  500. }
  501. publicTitleView.snp.makeConstraints { make in
  502. make.height.equalTo(cScreenHeigth)
  503. make.width.equalTo(cScreenWidth)
  504. make.bottom.equalToSuperview()
  505. }
  506. // 取消所有的导出
  507. PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
  508. exportSession.cancelExport()
  509. }
  510. // 开始导出
  511. appendAudio()
  512. /// 保存草稿
  513. saveDraftbox()
  514. // 曝光上报:窗口曝光
  515. PQEventTrackViewModel.baseReportUpload(businessType: .bt_windowView, objectType: .ot_view_publishSyncedUp, pageSource: .sp_stuck_publishSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-(曝光上报:窗口曝光)")
  516. //取推荐标题
  517. getTitles()
  518. }
  519. override func viewWillAppear(_ animated: Bool) {
  520. super.viewWillAppear(animated)
  521. PQNotification.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
  522. PQNotification.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
  523. UIApplication.shared.isIdleTimerDisabled = true
  524. //从相册选择一个照片后回调
  525. addNotification(self, selector: #selector(imageSelectedImage(notify:)), name: cSelectedImageSuccessKey, object: nil)
  526. #if swift(>=4.2)
  527. let memoryNotification = UIApplication.didReceiveMemoryWarningNotification
  528. _ = UIApplication.willTerminateNotification
  529. _ = UIApplication.didEnterBackgroundNotification
  530. #else
  531. let memoryNotification = NSNotification.Name.UIApplicationDidReceiveMemoryWarning
  532. let terminateNotification = NSNotification.Name.UIApplicationWillTerminate
  533. let enterbackgroundNotification = NSNotification.Name.UIApplicationDidEnterBackground
  534. #endif
  535. NotificationCenter.default.addObserver(
  536. self, selector: #selector(clearMemoryCache), name: memoryNotification, object: nil
  537. )
  538. }
  539. @objc public func clearMemoryCache() {
  540. BFLog(message: "收到内存警告")
  541. }
  542. override func viewWillDisappear(_ animated: Bool) {
  543. super.viewWillDisappear(animated)
  544. UIApplication.shared.isIdleTimerDisabled = false
  545. }
  546. deinit {
  547. view.endEditing(true)
  548. PQNotification.removeObserver(self)
  549. // 取消导出
  550. if exporter != nil {
  551. exporter.cancel()
  552. }
  553. avPlayer.pause()
  554. avPlayer.replaceCurrentItem(with: nil)
  555. // 点击上报:返回按钮
  556. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_back, pageSource: .sp_stuck_publishSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-(点击上报:返回按钮)")
  557. }
  558. }
  559. // MARK: - 导出/上传/下载及其他方法
  560. /// 导出/上传/下载及其他方法
  561. extension PQStuckPointPublicController {
  562. /// fp1 - 导出视频
  563. /// 开始导出视频
  564. /// 合并声音
  565. /// - Parameter urls: 所有音频的URL 是全路径方便复用
  566. /// - Parameter completeHander: 返回的 URL 全路径的 URL 如果要保存替换掉前缀
  567. func mergeAudios(originAsset: AVURLAsset, mTotalDuration: Float,clipAudioRange: CMTimeRange = CMTimeRange.zero,mStartTime:CMTime = .zero , completeHander: @escaping (_ fileURL: URL?) -> Void) {
  568. let timeInterval: TimeInterval = Date().timeIntervalSince1970
  569. let composition = AVMutableComposition()
  570. let originaDuration = CMTimeGetSeconds(clipAudioRange.duration)
  571. BFLog(message: "处理主音频 原始时长startTime = \(originaDuration) 要显示时长totalDuration = \(mTotalDuration)")
  572. if(originaDuration <= CMTimeGetSeconds(clipAudioRange.duration)){
  573. BFLog(message: "不用拼接音频文件 \(originAsset.url) 时长is \(CMTimeGetSeconds(originAsset.duration))")
  574. completeHander(originAsset.url)
  575. return
  576. }
  577. //整倍数
  578. let count = Int(mTotalDuration) / Int(originaDuration)
  579. //有余数多 clip 一整段
  580. let row = mTotalDuration - Float(count) * Float(originaDuration)
  581. //已经拼接的总时长
  582. var totalDuration: CMTime = .zero
  583. //第一段的时长
  584. var duration: CMTime = .zero
  585. //第一段的区间
  586. var timeRange:CMTimeRange = CMTimeRange.zero
  587. if count > 0 {
  588. for index in 0 ... count {
  589. duration = CMTime(value: CMTimeValue((CMTimeGetSeconds(clipAudioRange.end) - CMTimeGetSeconds(mStartTime)) * Double(playerTimescaleInt)), timescale: playerTimescaleInt)
  590. BFLog(message: "每一个文件的 duration \(CMTimeGetSeconds(duration))")
  591. var timeRange = CMTimeRangeMake(start: mStartTime, duration: duration)
  592. if(index != 0){
  593. //(CMTimeGetSeconds(clipAudioRange.end) - CMTimeGetSeconds(mStartTime))为用户选择的第一段时长
  594. duration = CMTime(value: CMTimeValue((CMTimeGetSeconds( clipAudioRange.duration) * Double(index) + (CMTimeGetSeconds(clipAudioRange.end) - CMTimeGetSeconds(mStartTime))) * Float64(playerTimescaleInt)), timescale: playerTimescaleInt)
  595. BFLog(message: "每一个文件的 duration \(CMTimeGetSeconds(duration))")
  596. timeRange = clipAudioRange
  597. }
  598. BFLog(message: "合并的文件地址: \(originAsset.url)")
  599. let audioAsset = originAsset
  600. let tracks = audioAsset.tracks(withMediaType: .audio)
  601. if tracks.count == 0 {
  602. BFLog(message: "音频数据无效不进行合并,所有任务结束要确保输入的数据都正常! \(originAsset.url)")
  603. break
  604. }
  605. let assetTrack: AVAssetTrack = tracks[0]
  606. let compositionAudioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID())!
  607. do {
  608. //
  609. try compositionAudioTrack.insertTimeRange(timeRange, of: assetTrack, at: totalDuration)
  610. } catch {
  611. BFLog(message: "error is \(error)")
  612. }
  613. totalDuration = CMTimeAdd(totalDuration, timeRange.duration)
  614. }
  615. }
  616. if(row > 0){
  617. duration = CMTime(value: CMTimeValue(Float(CMTimeGetSeconds(totalDuration)) * Float(playerTimescaleInt)), timescale: playerTimescaleInt)
  618. timeRange = CMTimeRange(start: duration, end: CMTime(value: CMTimeValue((CMTimeGetSeconds(duration) + Double(row)) * Double(playerTimescaleInt)), timescale: playerTimescaleInt))
  619. BFLog(message: "合并的文件地址: \(originAsset.url)")
  620. let audioAsset = originAsset
  621. let tracks = audioAsset.tracks(withMediaType: .audio)
  622. if tracks.count == 0 {
  623. BFLog(message: "音频数据无效不进行合并,所有任务结束要确保输入的数据都正常! \(originAsset.url)")
  624. }
  625. let assetTrack: AVAssetTrack = tracks[0]
  626. let compositionAudioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID())!
  627. do {
  628. //
  629. try compositionAudioTrack.insertTimeRange(timeRange, of: assetTrack, at: totalDuration)
  630. } catch {
  631. BFLog(message: "error is \(error)")
  632. }
  633. totalDuration = CMTimeAdd(totalDuration, audioAsset.duration)
  634. }
  635. let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)
  636. BFLog(message: "assetExport.supportedFileTypes is \(String(describing: assetExport?.supportedFileTypes))")
  637. assetExport?.outputFileType = .m4a
  638. // XXXX 注意文件名的后缀要和outputFileType 一致 否则会导出失败
  639. var audioFilePath = exportAudiosDirectory
  640. if !directoryIsExists(dicPath: audioFilePath) {
  641. BFLog(message: "文件夹不存在")
  642. createDirectory(path: audioFilePath)
  643. }
  644. audioFilePath.append("merge_\(timeInterval).m4a")
  645. let fileUrl = URL(fileURLWithPath: audioFilePath)
  646. assetExport?.outputURL = fileUrl
  647. assetExport?.exportAsynchronously {
  648. if assetExport!.status == .completed {
  649. // 85.819125
  650. let audioAsset = AVURLAsset(url: fileUrl, options: avAssertOptions)
  651. BFLog(message: "拼接声音文件 完成 \(fileUrl) 时长is \(CMTimeGetSeconds(audioAsset.duration))")
  652. completeHander(fileUrl)
  653. } else {
  654. print("拼接出错 \(String(describing: assetExport?.error))")
  655. completeHander(URL(string: ""))
  656. }
  657. }
  658. }
  659. func appendAudio() {
  660. let inputAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + (audioMixModel?.localPath ?? "")), options: nil)
  661. mergeAudios(originAsset: inputAsset, mTotalDuration: finallyUserAudioTime,clipAudioRange: clipAudioRange,mStartTime: CMTime(value: CMTimeValue((mStickers?.first?.timelineIn ?? 0.0) * Float64(playerTimescaleInt)), timescale: playerTimescaleInt)) { [weak self] completURL in
  662. if(completURL != nil){
  663. let asset = AVURLAsset(url: completURL!, options: nil)
  664. FilterLog(message: "拼接后音频时长\(asset.duration.seconds) url is \(String(describing: completURL))")
  665. self?.beginExport(inputAsset: asset)
  666. }
  667. }
  668. }
  669. func beginExport(inputAsset:AVURLAsset!) {
  670. if !(editProjectModel?.sData?.sections != nil && (editProjectModel?.sData?.sections.count ?? 0) > 0) {
  671. BFLog(message: "项目段落错误❌")
  672. return
  673. }
  674. // 输出视频地址
  675. var outPutMP4Path = exportVideosDirectory
  676. if !directoryIsExists(dicPath: outPutMP4Path) {
  677. BFLog(message: "文件夹不存在")
  678. createDirectory(path: outPutMP4Path)
  679. }
  680. outPutMP4Path.append("video_\(String.qe.timestamp()).mp4")
  681. let outPutMP4URL = URL(fileURLWithPath: outPutMP4Path)
  682. BFLog(message: "导出视频地址 \(outPutMP4URL)")
  683. // let inputAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + (audioMixModel?.localPath ?? "")), options: nil)
  684. // 每次初始化的时候设置初始值 为 nIl
  685. var audioMix: AVMutableAudioMix?
  686. var composition: AVMutableComposition?
  687. // if(finallyUserAudioTime != 0 && finallyUserAudioTime > Float(inputAsset.duration.seconds)){
  688. // (audioMix, composition) = PQPlayerViewModel.setupAudioMix(originAsset: inputAsset, bgmData:nil, videoStickers: nil,originMusicDuration:finallyUserAudioTime,clipAudioRange: clipAudioRange,startTime:CMTime(value: CMTimeValue((mStickers?.first?.timelineIn ?? 0.0) * Float64(playerTimescaleInt)), timescale: playerTimescaleInt))
  689. // }
  690. exporter = PQCompositionExporter(asset: inputAsset, videoComposition: nil, audioMix: audioMix, filters: nil, stickers: mStickers, animationTool: nil, exportURL: outPutMP4URL)
  691. var orgeBitRate = (editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * (editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 3
  692. if(mStickers != nil){
  693. for stick in mStickers! {
  694. if stick.type == StickerType.VIDEO.rawValue {
  695. let asset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + stick.locationPath), options: avAssertOptions)
  696. let cbr = asset.tracks(withMediaType: .video).first?.estimatedDataRate
  697. if Int(cbr ?? 0) > orgeBitRate {
  698. orgeBitRate = Int(cbr ?? 0)
  699. }
  700. }
  701. }
  702. }
  703. BFLog(message: "导出设置的码率为:\(orgeBitRate)")
  704. exporter.showGaussianBlur = true
  705. if exporter.prepare(videoSize: CGSize(width: editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0), videoAverageBitRate: orgeBitRate) {
  706. BFLog(message: "开始导出 \(String(describing: playeTimeRange.start)) 结束 \(String(describing: playeTimeRange.end))")
  707. exporter.start(playeTimeRange: playeTimeRange)
  708. BFLog(message: "开始导出")
  709. }
  710. exporter.progressClosure = { [weak self] _, _, progress in
  711. BFLog(message: "合成进度 \(progress)")
  712. let useProgress = progress > 1 ? 1 : progress
  713. if progress > 0, Int(useProgress * 100) > (self?.exportProgrss ?? 0) {
  714. // 更新进度
  715. self?.updatePublicCurrentProgress(useProgress: useProgress * 0.88)
  716. }
  717. }
  718. exporter.completion = { [weak self] url in
  719. BFLog(message: "MovieOutput total frames appended:导了完成: \(url) 生成视频时长为:\(CMTimeGetSeconds(AVAsset.init(url: url).duration))")
  720. // 导出完成后取消导出
  721. if self?.exporter != nil {
  722. self?.exporter.cancel()
  723. }
  724. self?.remindView?.removeFromSuperview()
  725. if !(self?.isExportSuccess ?? false) {
  726. self?.isExportSuccess = true
  727. self?.exportEndDate = Date().timeIntervalSince1970
  728. BFLog(message: "视频导出完成-开始去发布视频 总时长为\( (self?.exportEndDate ?? 0) - (self?.startExportDate ?? 0) * 1000)")
  729. self?.exportLocalURL = url
  730. /// fp2-1-1 - 请求权限
  731. // self?.authorizationStatus()
  732. /// fp2-2 - 保存草稿
  733. // self?.saveDraftbox()
  734. /// fp2 - 处理视频数据
  735. self?.dealWithVideoData()
  736. }
  737. }
  738. }
  739. /// fp2-1-1 - 请求权限
  740. func authorizationStatus() {
  741. let authStatus = PHPhotoLibrary.authorizationStatus()
  742. if authStatus == .notDetermined {
  743. // 第一次触发授权 alert
  744. PHPhotoLibrary.requestAuthorization { [weak self] (status: PHAuthorizationStatus) -> Void in
  745. if status != .authorized {
  746. cShowHUB(superView: nil, msg: "您尚未打开相册权限,请到设置页打开相册权限")
  747. } else {
  748. /// fp2-1-2 - 保存视频到相册
  749. self?.saveStuckPointVideo()
  750. }
  751. }
  752. } else if authStatus == .authorized {
  753. /// fp2-1-2 - 保存视频到相册
  754. saveStuckPointVideo()
  755. } else {
  756. // cShowHUB(superView: nil, msg: "您尚未打开相册权限,请到设置页打开相册权限")
  757. }
  758. }
  759. /// fp2-1-2 - 保存视频到相册
  760. /// - Parameter localPath: localPath description
  761. /// - Returns: <#description#>
  762. func saveStuckPointVideo() {
  763. let authStatus = PHPhotoLibrary.authorizationStatus()
  764. if authStatus == .authorized {
  765. let photoLibrary = PHPhotoLibrary.shared()
  766. photoLibrary.performChanges({ [weak self] in
  767. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: (self?.exportLocalURL)!)
  768. }) { [weak self] isFinished, _ in
  769. DispatchQueue.main.async { [weak self] in
  770. if self?.view != nil {
  771. if isFinished {
  772. // cShowHUB(superView: self!.view, msg: "视频已保存至相册")
  773. } else {
  774. // cShowHUB(superView: self!.view, msg: "视频保存失败")
  775. }
  776. }
  777. }
  778. }
  779. } else {
  780. // cShowHUB(superView: nil, msg: "您尚未打开相册权限,请到设置页打开相册权限")
  781. }
  782. }
  783. /// fp2-2 - 保存草稿
  784. /// - Returns: <#description#>
  785. @objc func saveDraftbox() {
  786. let sdata = editProjectModel?.sData?.toJSONString(prettyPrint: false)
  787. if sdata != nil, (sdata?.count ?? 0) > 0 {
  788. DispatchQueue.global().async { [weak self] in
  789. PQBaseViewModel.saveDraftbox(draftboxId: self?.editProjectModel?.draftboxId, title: self?.editProjectModel?.sData?.videoMetaData?.title, coverUrl: self?.editProjectModel?.sData?.videoMetaData?.coverUrl, sdata: sdata!, videoFromScene: .stuckPoint, copyType: (self?.audioMixModel != nil && self?.audioMixModel?.originProjectId != nil && (self?.audioMixModel?.originProjectId?.count ?? 0) > 0) ? 3 : nil, originProjectId: self?.audioMixModel?.originProjectId) { [weak self] draftboxInfo, _ in
  790. if draftboxInfo != nil {
  791. self?.editProjectModel?.draftboxId = draftboxInfo?["draftboxId"] as? String ?? ""
  792. self?.editProjectModel?.sData?.videoMetaData?.title = draftboxInfo?["title"] as? String ?? ""
  793. self?.editProjectModel?.sData?.videoMetaData?.coverUrl = draftboxInfo?["coverUrl"] as? String ?? ""
  794. self?.editProjectModel?.dataVersionCode = draftboxInfo?["dataVersionCode"] as? Int ?? 0
  795. BFLog(message: "保存远程的草稿成功")
  796. self?.isSaveDraftSuccess = true
  797. /// fp3 - 保存项目
  798. self?.saveProject()
  799. } else {
  800. // 保存草稿失败-播放视频
  801. // self?.publicEnd(isError: true)
  802. }
  803. }
  804. }
  805. } else {
  806. cShowHUB(superView: nil, msg: "您尚未打开相册权限,请到设置页打开相册权限")
  807. // 保存草稿失败-播放视频
  808. publicEnd(isError: true)
  809. }
  810. }
  811. /// fp3 - 保存项目
  812. /// - Returns: description
  813. func saveProject() {
  814. if isSaveDraftSuccess {
  815. let sdata = editProjectModel?.sData?.toJSONString(prettyPrint: false) ?? ""
  816. let draftboxId: String? = editProjectModel?.draftboxId
  817. PQBaseViewModel.saveProject(draftboxId: draftboxId, sdata: sdata, videoFromScene: .stuckPoint,rhythmMode: rhythmMode) { [weak self] projectId, msg in
  818. BFLog(message: "生成的项目id1111 :\(projectId ?? ""),msg = \(msg ?? "")")
  819. if projectId == nil || (projectId?.count ?? 0) <= 0 {
  820. PQBaseViewModel.saveProject(draftboxId: draftboxId, sdata: sdata, videoFromScene: .stuckPoint,rhythmMode: self?.rhythmMode ?? .createStickersModelPoint) { [weak self] projectId, msg in
  821. BFLog(message: "生成的项目id222 :\(projectId ?? ""),msg = \(msg ?? "")")
  822. if projectId == nil || (projectId?.count ?? 0) <= 0 {
  823. PQBaseViewModel.saveProject(draftboxId: draftboxId, sdata: sdata, videoFromScene: .stuckPoint,rhythmMode: self?.rhythmMode ?? .createStickersModelPoint) { [weak self] projectId, msg in
  824. BFLog(message: "生成的项目id 3333:\(projectId ?? ""),msg = \(msg ?? "")")
  825. if projectId != nil, (projectId?.count ?? 0) > 0 {
  826. self?.editProjectModel?.projectId = projectId ?? ""
  827. }
  828. /// fp4 - 处理视频数据
  829. // self?.dealWithVideoData()
  830. }
  831. } else {
  832. self?.editProjectModel?.projectId = projectId ?? ""
  833. /// fp4 - 处理视频数据
  834. // self?.dealWithVideoData()
  835. }
  836. }
  837. } else {
  838. self?.editProjectModel?.projectId = projectId ?? ""
  839. /// fp4 - 处理视频数据
  840. // self?.dealWithVideoData()
  841. }
  842. }
  843. }
  844. }
  845. /// fp4 - 处理视频数据
  846. /// - Returns: description
  847. @objc func dealWithVideoData() {
  848. BFLog(message: "开始去发布视频12")
  849. isSaveProjectSuccess = true
  850. if isExportSuccess && exportLocalURL != nil {
  851. BFLog(message: "素材上传完成同时视频导出完成开始发布视频")
  852. // 更新项目
  853. PQBaseViewModel.updateProject(projectId: editProjectModel?.projectId ?? "", produceStatus: "5") { repseon, _ in
  854. BFLog(message: "updateProject 结果 is \(String(describing: repseon))")
  855. }
  856. let asset = AVURLAsset(url: exportLocalURL!, options: nil)
  857. let tempUploadData = PQUploadModel()
  858. tempUploadData.duration = CMTimeGetSeconds(asset.duration)
  859. tempUploadData.localPath = exportLocalURL?.absoluteString
  860. tempUploadData.videoWidth = CGFloat(editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)
  861. tempUploadData.videoHeight = CGFloat(editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)
  862. tempUploadData.image = PQVideoSnapshotUtil.videoSnapshot(videoURL: exportLocalURL!, time: 0)
  863. if tempUploadData.image == nil {
  864. tempUploadData.image = coverImage
  865. }
  866. tempUploadData.videoFromScene = .stuckPoint
  867. eventTrackData = getExportEventTrackData()
  868. eventTrackData?.projectId = editProjectModel?.projectId ?? ""
  869. uploadData = tempUploadData
  870. if uploadData?.image == nil {
  871. uploadData?.image = PQVideoSnapshotUtil.videoSnapshot(videoURL: exportLocalURL!, time: 0)
  872. }
  873. if uploadData?.image != nil {
  874. playerHeaderView.image = uploadData?.image
  875. coverImageView.image = uploadData?.image
  876. }
  877. if isExportSuccess, exportLocalURL != nil {
  878. let size = try! exportLocalURL?.resourceValues(forKeys: [.fileSizeKey])
  879. BFLog(message: "size = \(String(describing: size))")
  880. if Float64(size?.fileSize ?? 0) <= maxUploadSize {
  881. /// fp5 - 上传视频
  882. reUploadVideo()
  883. }
  884. }
  885. }
  886. }
  887. /// fp5 - 上传视频
  888. /// - Returns: <#description#>
  889. @objc func reUploadVideo() {
  890. if uploadData?.stsToken != nil {
  891. multipartUpload(response: uploadData?.stsToken)
  892. } else {
  893. uploadVideo()
  894. }
  895. }
  896. /// fp5-1 - 开始上传视频
  897. /// - Returns: <#description#>
  898. func uploadVideo() {
  899. let uploadRequest: OSSMultipartUploadRequest? = PQAliOssUtil.shared.allTasks[uploadData?.videoBucketKey ?? ""]
  900. if uploadRequest != nil, "\(uploadRequest?.callbackParam["code"] ?? "0")" == "1" {
  901. return
  902. }
  903. // 更新进度
  904. updatePublicCurrentProgress(useProgress: 0.89)
  905. DispatchQueue.global().async {
  906. PQBaseViewModel.getStsToken { [weak self] response, _ in
  907. if response == nil {
  908. self?.showUploadRemindView(isNetCollected: false, msg: "获取数据失败了哦~")
  909. return
  910. }
  911. // 更新进度
  912. self?.updatePublicCurrentProgress(useProgress: 0.90)
  913. BFLog(message: "取我方服务器STS 返回数据 \(String(describing: response))")
  914. self?.multipartUpload(response: response)
  915. }
  916. }
  917. }
  918. /// fp5-2 - 继续上传视频
  919. /// - Parameter response: <#response description#>
  920. func multipartUpload(response: [String: Any]?) {
  921. let FileName: String = "\(response?["FileName"] ?? "")"
  922. let uploadID: String = "\(response?["Upload"] ?? "")"
  923. uploadData?.stsToken = response
  924. uploadData?.videoBucketKey = FileName
  925. uploadData?.uploadID = uploadID
  926. if uploadData?.asset != nil && isValidURL(url: uploadData?.localPath) {
  927. PQPHAssetVideoParaseUtil.exportPHAssetToMP4(phAsset: (uploadData?.asset)!, isCancelCurrentExport: true) { [weak self] _, _, filePath, _ in
  928. if filePath != nil, (filePath?.count ?? 0) > 0 {
  929. self?.uploadData?.localPath = filePath
  930. PQAliOssUtil.multipartUpload(localPath: self?.uploadData?.localPath ?? "", response: response)
  931. }
  932. }
  933. } else {
  934. PQAliOssUtil.multipartUpload(localPath: uploadData?.localPath ?? "", response: response)
  935. }
  936. PQAliOssUtil.shared.aliOssHander = { [weak self] isMatarialUpload, materialType, _, code, objectkey, _, _, _, _, _, _, _, _, _ in
  937. if !isMatarialUpload, materialType == .VIDEO, self?.uploadData?.videoBucketKey == objectkey {
  938. if code == 6 { // 无网
  939. let uploadRequest: OSSMultipartUploadRequest? = PQAliOssUtil.shared.allTasks[self?.uploadData?.videoBucketKey ?? ""]
  940. if !(uploadRequest != nil && "\(uploadRequest?.callbackParam["code"] ?? "0")" == "1") {
  941. self?.showUploadRemindView()
  942. }
  943. } else if code == 260 {
  944. self?.showUploadRemindView(isNetCollected: false)
  945. } else if code != 1 {
  946. // 上传失败-播放视频
  947. self?.publicEnd(isError: true)
  948. }
  949. }
  950. }
  951. PQAliOssUtil.shared.aliOssProgressHander = { [weak self] bytesSent, totalBytesSent, totalBytesExpectedToSend, _, _ in
  952. let progress: Float = 0.90 + Float(Float(totalBytesSent) / Float(totalBytesExpectedToSend)) * 0.09
  953. BFLog(message: "卡点视频上传:bytesSent = \(bytesSent),totalBytesSent = \(totalBytesSent),totalBytesExpectedToSend = \(totalBytesExpectedToSend),progress = \(progress)")
  954. if progress >= 0.90, progress <= 0.99 {
  955. // 更新进度
  956. self?.updatePublicCurrentProgress(useProgress: progress)
  957. }
  958. }
  959. }
  960. /// fp6 - 视频上传成功,处理要发布视频数据
  961. /// - Parameter notify: <#notify description#>
  962. @objc func uploadSuccess(notify: NSNotification) {
  963. let objectKey: String = "\(notify.userInfo?["objectKey"] ?? "")"
  964. BFLog(message: "收到上传成功请求==\(notify.userInfo ?? [:])")
  965. if uploadData?.videoBucketKey == objectKey {
  966. // 上传成功
  967. isUploadSuccess = true
  968. /// fp7 - 处理要发布视频数据
  969. dealWithPublicData()
  970. }
  971. }
  972. /// fp7 - 处理要发布视频数据
  973. /// - Returns: <#description#>
  974. func dealWithPublicData() {
  975. if uploadData?.localPath != nil {
  976. let size = try! URL(string: uploadData?.localPath ?? "")?.resourceValues(forKeys: [.fileSizeKey])
  977. BFLog(message: "size = \(String(describing: size))")
  978. if Float64(size?.fileSize ?? 0) > maxUploadSize {
  979. cShowHUB(superView: nil, msg: "无法发布大于10G的视频,请重新选择/合成发布")
  980. // 上传失败-播放视频
  981. publicEnd(isError: true)
  982. return
  983. }
  984. }
  985. let projectId: String? = editProjectModel?.projectId
  986. let uploadRequest: OSSMultipartUploadRequest? = PQAliOssUtil.shared.allTasks[uploadData?.videoBucketKey ?? ""]
  987. if uploadRequest == nil {
  988. reUploadVideo()
  989. return
  990. }
  991. let tempModel = PQVideoListModel()
  992. tempModel.title = selectTitle
  993. tempModel.summary = ""
  994. tempModel.duration = CGFloat(uploadData?.duration ?? 0)
  995. tempModel.uplpadImage = uploadData?.image
  996. tempModel.uplpadBucketKey = uploadRequest?.objectKey
  997. tempModel.localPath = uploadData?.localPath
  998. tempModel.reCreateVideoData = reCreateData
  999. tempModel.eventTrackData = eventTrackData
  1000. tempModel.uplpadStatus = 1
  1001. tempModel.videoFromScene = .stuckPoint
  1002. tempModel.uid = Int(BFLoginUserInfo.shared.uid) ?? 0
  1003. tempModel.uplpadRequest = PQAliOssUtil.shared.allTasks[uploadData?.videoBucketKey ?? ""]
  1004. tempModel.stsToken = uploadData?.stsToken
  1005. tempModel.projectId = projectId
  1006. /// fp8 - 发布视频
  1007. publicVideo(videoData: tempModel)
  1008. }
  1009. /// fp8 - 发布视频
  1010. /// - Parameter videoData: <#videoData description#>
  1011. func publicVideo(videoData: PQVideoListModel) {
  1012. if videoData.uplpadBucketKey == nil {
  1013. BFLog(message: "发布视频:视频uplpadBucketKey为空-\(String(describing: videoData.uplpadBucketKey))")
  1014. // 上传失败-播放视频
  1015. publicEnd(isError: true)
  1016. return
  1017. }
  1018. BFLog(message: "开始发布")
  1019. if (videoData.eventTrackData?.endUploadDate ?? 0) <= 0 {
  1020. // 结束上传时间
  1021. videoData.eventTrackData?.endUploadDate = Date().timeIntervalSince1970
  1022. }
  1023. DispatchQueue.global().async {
  1024. // PQBaseViewModel.ossTempToken { [weak self] response, _ in
  1025. // let image: UIImage = videoData.uplpadImage ?? UIImage()
  1026. // let data = image.jpegData(compressionQuality: 1)
  1027. // let accessKeyId: String = "\(response?["accessKeyId"] ?? "")"
  1028. // let secretKeyId: String = "\(response?["accessKeySecret"] ?? "")"
  1029. // let securityToken: String = "\(response?["securityToken"] ?? "")"
  1030. // let endpoint: String = "\(response?["uploadDomain"] ?? "")"
  1031. // let bucketName: String = "\(response?["bucketName"] ?? "")"
  1032. // let objectKey: String = "\(response?["objectKey"] ?? "")"
  1033. // BFLog(message: "开始上传视频图片==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? ""),objectKey =\(objectKey)")
  1034. // PQAliOssUtil.shared
  1035. // .startClient(
  1036. // accessKeyId: accessKeyId,
  1037. // secretKeyId: secretKeyId,
  1038. // securityToken: securityToken,
  1039. // endpoint: endpoint
  1040. // )
  1041. // .uploadObjectAsync(bucketName: bucketName, objectKey: objectKey, data: data!, fileExtensions: "png", imageUploadBlock: { _, code, ossObjectKey, _ in
  1042. // BFLog(message: "图片上传完成==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? ""),objectKey =\(objectKey),ossObjectKey = \(ossObjectKey)")
  1043. // if code == 1 && ossObjectKey == objectKey && objectKey.count > 0 {
  1044. // BFLog(message: "开始发布==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? ""),objectKey =\(objectKey),ossObjectKey = \(ossObjectKey)")
  1045. PQUploadViewModel.publishVideo(projectId: videoData.projectId, fileExtensions: videoData.localPath?.pathExtension, title: videoData.title ?? "", videoPath: videoData.uplpadBucketKey ?? "", coverImgPath: nil, descr: videoData.summary ?? "", videoFromScene: .stuckPoint, reCreateData: videoData.reCreateVideoData, eventTrackData: videoData.eventTrackData) { [weak self] newVideoData, _, _ in
  1046. self?.videoData = newVideoData
  1047. self?.videoData?.title = self?.titleLabel.text
  1048. if self?.videoData?.reCreateVideoData == nil {
  1049. let reCreateVideo = PQReCreateModel()
  1050. reCreateVideo.reProduceVideoFlag = 1
  1051. self?.videoData?.reCreateVideoData = reCreateVideo
  1052. }
  1053. postNotification(name: cPublishStuckPointSuccessKey, userInfo: ["newVideoData": self?.videoData ?? PQVideoListModel()])
  1054. BFLog(message: "发布成功==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? "")")
  1055. // cShowHUB(superView: nil, msg: "视频发布成功")
  1056. // 发布成功后续操作
  1057. self?.publicEnd()
  1058. PQEventTrackViewModel.publishReportUpload(projectId: videoData.projectId, businessType: .bt_publish_success, ossInfo: videoData.stsToken ?? [:], params: ["title": videoData.title ?? "", "videoPath": videoData.uplpadBucketKey ?? "", "descr": videoData.summary ?? ""])
  1059. }
  1060. // } else {
  1061. // // 图片上传失败
  1062. // BFLog(message: "图片上传失败重新发布视频==\(videoData.title ?? ""),\(videoData.uplpadBucketKey ?? "")")
  1063. // self?.publicVideo(videoData: videoData)
  1064. // }
  1065. // })
  1066. // }
  1067. }
  1068. }
  1069. /// 发布结束操作
  1070. /// - Parameter isError: <#isError description#>
  1071. /// - Returns: <#description#>
  1072. func publicEnd(isError: Bool = false) {
  1073. UIApplication.shared.keyWindow?.viewWithTag(100_100)?.removeFromSuperview()
  1074. isPublicSuccess = true
  1075. progressView.removeFromSuperview()
  1076. progressTipsLab.removeFromSuperview()
  1077. oprationBgView.removeFromSuperview()
  1078. playBtn.isHidden = true
  1079. avPlayer.replaceCurrentItem(with: AVPlayerItem(url: URL(fileURLWithPath: (exportLocalURL?.absoluteString ?? "").replacingOccurrences(of: "file:///", with: ""))))
  1080. avPlayer.play()
  1081. if isError {
  1082. cShowHUB(superView: nil, msg: "视频发布失败,请重新合成")
  1083. } else {
  1084. bottomOprationBgView.isHidden = false
  1085. /// fp2-1-1 - 请求权限
  1086. authorizationStatus()
  1087. }
  1088. }
  1089. /// 生成创作工具埋点数据
  1090. /// - Returns: <#description#>
  1091. func getExportEventTrackData() -> PQVideoMakeEventTrackModel? {
  1092. let eventTrackData = PQVideoMakeEventTrackModel(projectModel: editProjectModel, reCreateData: reCreateData)
  1093. eventTrackData.entrance = .entranceStuckPointPublic
  1094. eventTrackData.editTimeCost = 0
  1095. eventTrackData.composeTimeCost = (exportEndDate - startExportDate) * 1000
  1096. eventTrackData.musicName = audioMixModel?.musicName ?? ""
  1097. eventTrackData.syncedUpMusicName = audioMixModel?.musicName ?? ""
  1098. eventTrackData.musicId = audioMixModel?.musicId ?? ""
  1099. eventTrackData.syncedUpMusicId = audioMixModel?.musicId ?? ""
  1100. eventTrackData.musicUrl = audioMixModel?.selectVoiceType == 1 ? (audioMixModel?.musicPath ?? "") : (audioMixModel?.accompanimentPath ?? "")
  1101. eventTrackData.musicType = audioMixModel != nil ? (audioMixModel?.selectVoiceType == 1 ? "original" : "accompaniment") : ""
  1102. eventTrackData.isMusicClip = (audioMixModel?.startTime ?? 0) > 0
  1103. if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.origin.rawValue {
  1104. eventTrackData.canvasRatio = "original"
  1105. } else if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.nineToSixteen.rawValue {
  1106. eventTrackData.canvasRatio = "9:16"
  1107. } else if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.oneToOne.rawValue {
  1108. eventTrackData.canvasRatio = "1:1"
  1109. } else if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.sixteenToNine.rawValue {
  1110. eventTrackData.canvasRatio = "16:9"
  1111. }
  1112. eventTrackData.syncedUpVideoNumber = selectedDataCount - selectedImageDataCount
  1113. eventTrackData.syncedUpImageNumber = selectedImageDataCount
  1114. eventTrackData.syncedUpOriginalMaterialDuration = selectedTotalDuration * 1000
  1115. eventTrackData.syncedUpRhythmNumber = audioMixModel?.speed ?? 2
  1116. eventTrackData.syncedUpVideoDuration = ((audioMixModel?.endTime ?? 0) - (audioMixModel?.startTime ?? 0)) * 1000
  1117. //add by ak
  1118. eventTrackData.syncedUpVideoType = rhythmMode
  1119. eventTrackData.syncedUpVideoSpeedMax = syncedUpVideoSpeedMax
  1120. eventTrackData.syncedUpVideoSpeedMin = syncedUpVideoSpeedMin
  1121. return eventTrackData
  1122. }
  1123. /// 播放视频
  1124. /// - Returns: description
  1125. @objc func playVideo() {
  1126. playBtn.isHidden = !playBtn.isHidden
  1127. changPlayerIsPause(isPause: !playBtn.isHidden)
  1128. }
  1129. /// 按钮点击事件
  1130. /// - Parameter sender: <#sender description#>
  1131. /// - Returns: <#description#>
  1132. @objc func btnClick(sender: UIButton) {
  1133. switch sender.tag {
  1134. case 1:
  1135. if !(isExportSuccess && isSaveProjectSuccess && isUploadSuccess && isPublicSuccess) {
  1136. cShowHUB(superView: nil, msg: "视频发布失败,请重新合成")
  1137. return
  1138. }
  1139. if !PQSingletoWXApiUtil.shared.isInstallWX() {
  1140. cShowHUB(superView: nil, msg: "您还未安装微信客户端!")
  1141. return
  1142. }
  1143. cShowHUB(superView: nil, msg: nil)
  1144. let shareId = getUniqueId(desc: "\(videoData?.uniqueId ?? "")shareId")
  1145. PQBaseViewModel.wxFriendShareInfo(videoId: (videoData?.uniqueId)!) { [weak self] imagePath, title, shareWeappRawId, msg in
  1146. if msg != nil {
  1147. cShowHUB(superView: nil, msg: "网络不佳哦")
  1148. return
  1149. }
  1150. self?.isShared = true
  1151. PQSingletoWXApiUtil.shared.share(type: 3, scene: Int32(WXSceneSession.rawValue), shareWeappRawId: shareWeappRawId, title: title, description: title, imageUrl: imagePath, path: self?.videoData?.videoPath, videoId: (self?.videoData?.uniqueId)!, pageSource: self?.videoData?.pageSource ?? .sp_category, shareId: shareId).wxApiUtilHander = { _, _ in
  1152. }
  1153. cHiddenHUB(superView: nil)
  1154. }
  1155. // 点击上报:分享微信
  1156. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_shareWechat, pageSource: .sp_stuck_publishSyncedUp, extParams: ["videoId": videoData?.uniqueId ?? ""], remindmsg: "卡点视频数据上报-(点击上报:分享微信)")
  1157. case 2:
  1158. if !(isExportSuccess && isSaveProjectSuccess && isUploadSuccess && isPublicSuccess) {
  1159. cShowHUB(superView: nil, msg: "视频发布失败,请重新合成")
  1160. return
  1161. }
  1162. if !PQSingletoWXApiUtil.shared.isInstallWX() {
  1163. cShowHUB(superView: nil, msg: "您还未安装微信客户端!")
  1164. return
  1165. }
  1166. let shareId = getUniqueId(desc: "\(videoData?.uniqueId ?? "")shareId")
  1167. PQBaseViewModel.h5ShareLinkInfo(videoId: videoData?.uniqueId ?? "", pageSource: videoData?.pageSource ?? .sp_category) { [weak self] path, _ in
  1168. cHiddenHUB(superView: nil)
  1169. if path != nil {
  1170. PQBaseViewModel.wxFriendShareInfo(videoId: (self?.videoData?.uniqueId)!) { [weak self] imagePath, title, shareWeappRawId, msg in
  1171. if msg != nil {
  1172. cShowHUB(superView: nil, msg: "网络不佳哦")
  1173. return
  1174. }
  1175. self?.isShared = true
  1176. PQSingletoWXApiUtil.shared.share(type: 1, scene: Int32(WXSceneTimeline.rawValue), title: self?.videoData?.title ?? "\(BFLoginUserInfo.shared.nickName)made a music video for you", description: "", imageUrl: imagePath, path: path, videoId: (self?.videoData?.uniqueId)!, pageSource: self?.videoData?.pageSource ?? .sp_category, shareId: shareId).wxApiUtilHander = { _, _ in
  1177. }
  1178. }
  1179. } else {
  1180. cShowHUB(superView: nil, msg: "网络不佳哦")
  1181. }
  1182. }
  1183. // 点击上报:分享朋友圈
  1184. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_shareWechatMoment, pageSource: .sp_stuck_publishSyncedUp, extParams: ["videoId": videoData?.uniqueId ?? ""], remindmsg: "卡点视频数据上报-(点击上报:分享朋友圈)")
  1185. case 3:
  1186. // 点击上报:完成
  1187. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_finished, pageSource: .sp_stuck_publishSyncedUp, extParams: ["videoId": videoData?.uniqueId ?? ""], remindmsg: "卡点视频数据上报-(点击上报:完成)")
  1188. navigationController?.viewControllers = [(navigationController?.viewControllers.first)!]
  1189. // 发送通知
  1190. postNotification(name: cFinishedPublishedNotiKey)
  1191. default:
  1192. break
  1193. }
  1194. }
  1195. /// 添加提示视图
  1196. /// - Parameters:
  1197. /// - isNetCollected: <#isNetCollected description#>
  1198. /// - msg: <#msg description#>
  1199. func showUploadRemindView(isNetCollected _: Bool = true, msg _: String? = nil) {
  1200. view.endEditing(true)
  1201. // PQUploadRemindView.showUploadRemindView(title: isNetCollected ? "上传中断" : "上传失败", summary: (isNetCollected ? "似乎已断开与互联网的连接" : (msg != nil ? msg : "视频文件已丢失"))!, confirmTitle: isNetCollected ? "重新连接网络" : "重新上传") { [weak self] _, _ in
  1202. // if isNetCollected {
  1203. // openAppSetting()
  1204. // } else {
  1205. // self?.navigationController?.popToViewController((self?.navigationController?.viewControllers[1])!, animated: true)
  1206. // }
  1207. // }
  1208. }
  1209. @objc func enterBackground() {
  1210. BFLog(message: "进入到后台")
  1211. // 取消导出
  1212. if exporter != nil {
  1213. exporter.cancel()
  1214. }
  1215. playBtn.isHidden = false
  1216. avPlayer.pause()
  1217. }
  1218. @objc func willEnterForeground() {
  1219. BFLog(message: "进入到前台")
  1220. if !isExportSuccess {
  1221. appendAudio()
  1222. }
  1223. playBtn.isHidden = true
  1224. avPlayer.play()
  1225. }
  1226. @objc func didBecomeActiveNotification() {
  1227. if isShared {
  1228. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { [weak self] in
  1229. self?.isShared = false
  1230. cShowHUB(superView: nil, msg: "分享成功")
  1231. self?.playBtn.isHidden = true
  1232. self?.avPlayer.play()
  1233. }
  1234. }
  1235. }
  1236. /// 更新进度
  1237. /// - Returns: <#description#>
  1238. func updatePublicCurrentProgress(useProgress: Float) {
  1239. exportProgrss = Int(useProgress * 100)
  1240. progressView.setProgress(useProgress, animated: true)
  1241. let attributedText = NSMutableAttributedString(string: "\(exportProgrss)%\n视频正在处理中,请勿离开")
  1242. attributedText.addAttributes([.font: UIFont.systemFont(ofSize: 34)], range: NSRange(location: 0, length: "\(exportProgrss)%".count))
  1243. progressTipsLab.attributedText = attributedText
  1244. }
  1245. func changPlayerIsPause(isPause:Bool) {
  1246. if(isPause){
  1247. playBtn.isHidden = false
  1248. avPlayer.pause()
  1249. playerHeaderCoverImageView.isHidden = false
  1250. }else{
  1251. playBtn.isHidden = true
  1252. avPlayer.play()
  1253. playerHeaderCoverImageView.isHidden = true
  1254. }
  1255. }
  1256. @objc func titleLabelClick() {
  1257. BFLog(message: "点击输入框")
  1258. changPlayerIsPause(isPause: true)
  1259. pinView.isHidden = true
  1260. publicTitleView.show()
  1261. if(publicTitleView.inputTV.text.count > 0){
  1262. publicTitleView.inputTV.text = titleLabel.text
  1263. }
  1264. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_clickButton_changeTitle, pageSource: .sp_stuck_publishSyncedUp, eventData: ["videoId":videoData?.uniqueId ?? "","rootPageSource":isReCreate ? "shanyinApp-main-syncedUpMusicRecreate" :"shanyinApp-main-syncedUpMusic"], remindmsg: "")
  1265. }
  1266. @objc func settingCoverImage() {
  1267. if(exportLocalURL == nil){
  1268. BFLog(message: "导出的视频地址错误???。。。")
  1269. return
  1270. }
  1271. changPlayerIsPause(isPause: true)
  1272. let asset = AVURLAsset(url: exportLocalURL!, options: nil)
  1273. publicEditCoverView.show(videoURL: exportLocalURL!, duration: CMTimeGetSeconds(asset.duration))
  1274. //点击了确认 btn
  1275. publicEditCoverView.selectImageCallBack = { [weak self] imageData in
  1276. self?.changPlayerIsPause(isPause: false)
  1277. if(imageData != nil){
  1278. self?.coverImageView.image = imageData
  1279. self?.playerHeaderCoverImageView.image = imageData
  1280. self?.uploadData?.image = imageData
  1281. self?.updateCoverImagegOrTitle()
  1282. }
  1283. }
  1284. //点击了从相册选择
  1285. publicEditCoverView.selectPhotoBtnCallBack = { [weak self] in
  1286. let imageSelected = PQImageSelectedController()
  1287. imageSelected.isAssetImage = true
  1288. imageSelected.videoWidth = CGFloat(self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)
  1289. imageSelected.videoHeight = CGFloat(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)
  1290. // imageSelected.uploadData = uploadData
  1291. // imageSelected.updataVideoData = updataVideoData
  1292. self?.navigationController?.pushViewController(imageSelected, animated: true)
  1293. }
  1294. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_clickButton_changeCover, pageSource: .sp_stuck_publishSyncedUp, eventData: ["videoId":videoData?.uniqueId ?? "","rootPageSource":isReCreate ? "shanyinApp-main-syncedUpMusicRecreate" :"shanyinApp-main-syncedUpMusic"], remindmsg: "")
  1295. }
  1296. //更新标题或封面
  1297. func updateCoverImagegOrTitle() {
  1298. PQLoadingHUB.shared.showHUB(isMode: true)
  1299. PQBaseViewModel.ossTempToken { [weak self] response, msg in
  1300. let image: UIImage = (self?.uploadData?.image)!
  1301. let data = image.jpegData(compressionQuality: 1)
  1302. let accessKeyId: String = "\(response?["accessKeyId"] ?? "")"
  1303. let secretKeyId: String = "\(response?["accessKeySecret"] ?? "")"
  1304. let securityToken: String = "\(response?["securityToken"] ?? "")"
  1305. let endpoint: String = "\(response?["endPoint"] ?? "")"
  1306. let bucketName: String = "\(response?["bucketName"] ?? "")"
  1307. let objectKey: String = "\(response?["objectKey"] ?? "")"
  1308. PQAliOssUtil.shared
  1309. .startClient(
  1310. accessKeyId: accessKeyId,
  1311. secretKeyId: secretKeyId,
  1312. securityToken: securityToken,
  1313. endpoint: endpoint
  1314. )
  1315. .uploadObjectAsync(bucketName: bucketName, objectKey: objectKey, data: data!, fileExtensions: "png", imageUploadBlock: { _, code, ossObjectKey, _ in
  1316. if code == 1 && ossObjectKey == objectKey && ossObjectKey.count > 0 {
  1317. //add by ak 这里会在服务器生成分享使用的图片到1-2S 时间
  1318. PQUploadViewModel.updateVideo(title: self?.videoData?.title ?? "", videoId: self?.videoData?.uniqueId ?? "", coverImgPath: objectKey, descr: "") { newVideoData, msg in
  1319. if newVideoData == nil {
  1320. cShowHUB(superView: self?.view, msg: msg)
  1321. //可能有敏感词 要刷一组新标题并自动更新
  1322. let numberRandom: UInt32 = UInt32(arc4random_uniform(UInt32( self?.publicTitleView.titles.count ?? 0)))
  1323. self?.setTitleText(text: self?.publicTitleView.titles[Int(numberRandom)] ?? "")
  1324. self?.updateCoverImagegOrTitle()
  1325. sleep(UInt32(1.5))
  1326. PQLoadingHUB.shared.dismissHUB()
  1327. return
  1328. }else{
  1329. PQLoadingHUB.shared.dismissHUB()
  1330. }
  1331. }
  1332. } else {
  1333. PQLoadingHUB.shared.dismissHUB()
  1334. }
  1335. })
  1336. }}
  1337. func setTitleText(text:String ,textColor:UIColor = UIColor.hexColor(hexadecimal: "#ABABAB")) {
  1338. selectTitle = text
  1339. //更新 UI
  1340. titleLabel.text = text
  1341. titleLabel.textColor = textColor
  1342. publicTitleView.inputTV.placeHolder = text
  1343. //更新数据
  1344. videoData?.title = text
  1345. }
  1346. //取推荐的10个标题
  1347. func getTitles(){
  1348. PQBaseViewModel.getBaseConfig(completeHander: {[weak self] titles in
  1349. if((titles?.count ?? 0) > 0){
  1350. self?.publicTitleView.titles = titles!
  1351. let numberRandom: UInt32 = UInt32(arc4random_uniform(UInt32(titles!.count)))
  1352. BFLog(message: "接收到的 titles\(String(describing: titles))")
  1353. self?.setTitleText(text: titles?[Int(numberRandom)] ?? "")
  1354. }
  1355. })
  1356. }
  1357. @objc func imageSelectedImage(notify: Notification) {
  1358. let imageData: UIImage? = notify.userInfo?["image"] as? UIImage
  1359. if imageData != nil {
  1360. changPlayerIsPause(isPause: false)
  1361. BFLog(message: "从系统相册选择了一个照片")
  1362. publicEditCoverView.isHidden = true
  1363. coverImageView.image = imageData
  1364. playerHeaderCoverImageView.image = imageData
  1365. uploadData?.image = imageData
  1366. updateCoverImagegOrTitle()
  1367. }
  1368. }
  1369. }