PQStuckPointPublicController.swift 51 KB

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