PQStuckPointPublicController.swift 63 KB

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