PQStuckPointPublicController.swift 73 KB

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