PQStuckPointPublicController.swift 60 KB

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