//
//  PQStuckPointPublicController.swift
//  PQSpeed
//
//  Created by SanW on 2021/5/6.
//  Copyright © 2021 BytesFlow. All rights reserved.
//

import ObjectMapper
import Photos
import UIKit
import WechatOpenSDK
import Kingfisher
import BFCommonKit
import Alamofire


//mdf by ak 按 UI图 下方操作区的高度是固定的， 其它区高度和设备自适应
public let bottomOprationBgViewHeight:CGFloat = 322.0

class PQStuckPointPublicController: PQBaseViewController {
    private var isShared: Bool = false // 是否在分享
    private var isExportSuccess: Bool = false // 是否导出完成
    private var isSaveDraftSuccess: Bool = false // 是否保存草稿完成
    private var isSaveProjectSuccess: Bool = false // 是否保存项目完成
    private var isUploadSuccess: Bool = false // 是否上传完成
    private var isPublicSuccess: Bool = false // 是否发布完成
    private var exportLocalURL: URL? // 导出的地址
    // 再创作数据
    private var reCreateData: PQReCreateModel?
    // 确定上传的数据
    private var uploadData: PQUploadModel?
    // 发布成功的视频数据
    private var videoData: PQVideoListModel?
    // 视频创作埋点数据
    private var eventTrackData: PQVideoMakeEventTrackModel?
    // 选中的总时长-统计使用
    var selectedTotalDuration: Float64 = 0
    // 选择的总数-统计使用
    var selectedDataCount: Int = 0
    // 选择的图片总数-统计使用
    var selectedImageDataCount: Int = 0

    // 最大的宽度
    private var maxWidth: CGFloat = cScreenWidth
    // 最大的高度
    private var maxHeight: CGFloat = cScreenHeigth - bottomOprationBgViewHeight - cDevice_iPhoneNavBarAndStatusBarHei
    // 开始导出的时间
    private let startExportDate: Float64 = Date().timeIntervalSince1970
    // 导出结束的时间
    private var exportEndDate: Float64 = Date().timeIntervalSince1970
    // 取到的封面 给发布界面使用
    private var coverImage: UIImage?
    // 导出视频工具类
    private var exporter: PQCompositionExporter!
    // 导出进度
    private var exportProgrss = 0
    var mStickers: [PQEditVisionTrackMaterialsModel]?

    var remindView: PQRemindView?
    // 已经选择标题内容，加一个属性接收 使用有不在主线不能直接使用 titleLabel text
    var selectTitle: String = ""

    // add by ak 玩法类型 调用 producevideo/saveProject 时使用
    var rhythmMode: createStickersModel = .createStickersModelPoint

    // add by ak 设置的速度
    var syncedUpVideoSpeedMax: Float = 0.0
    var syncedUpVideoSpeedMin: Float = 0.0

    // add by ak 是否是再创作模式
    var isReCreate: Bool = false

    // 最终使用的音频时长,用于拼接音乐使用
    var finallyUserAudioTime: Float = 0.0
    // 拼接音乐的开始和结束位置
    var clipAudioRange: CMTimeRange = CMTimeRange.zero
    // 导出的开始的开始和结束时间
    var playeTimeRange: CMTimeRange = CMTimeRange()
    
    //---------------------------add by ak 保存系统相册使用的变量
    // 导出有水印的正片
    private var watermarkMovieExporter: PQCompositionExporter!
    // 带水印 MP4 导出地址
    private var watermarkMovieLocalURL: URL?
    // 导出片尾
    private var endMovieExporter: PQCompositionExporter!
    // 导出片尾 MP4 地址
    private var endMovieLocalURL: URL?
    // 保存相册的合成视频地址 水印+片尾 MP4 地址
    private var saveMovieLocalURL: URL?
    
    private var isSaveingLocalVideo = false
  
    //----------------------------

    // 预览大小
    private var preViewSize: CGSize {
        switch aspectRatio {
        case let .origin(width, height):
            var tempHeight: CGFloat = 0
            var tempWidth: CGFloat = 0
            if width > height {
                tempWidth = maxWidth
                tempHeight = (maxWidth * height / width)
                if tempHeight > maxHeight {
                    tempHeight = maxHeight
                    tempWidth = (maxHeight * width / height)
                }
            } else {
                tempHeight = maxHeight
                tempWidth = (maxHeight * width / height)
                if tempWidth > maxWidth {
                    tempWidth = maxWidth
                    tempHeight = (maxWidth * height / width)
                }
            }
            if tempHeight.isNaN || tempWidth.isNaN {
                return CGSize.zero
            } else {
                return CGSize(width: tempWidth, height: tempHeight)
            }
        case .oneToOne:
            if maxWidth > maxHeight {
                return CGSize(width: maxHeight, height: maxHeight)
            } else {
                return CGSize(width: maxWidth, height: maxWidth)
            }
        case .sixteenToNine:
            return CGSize(width: maxWidth, height: maxWidth * 9.0 / 16.0)
        case .nineToSixteen:
            return CGSize(width: maxHeight * 9.0 / 16.0, height: maxHeight)
        default:
            break
        }
        return CGSize(width: maxHeight, height: maxHeight)
    }

    // 背景音乐
    var audioMixModel: PQVoiceModel?
    // 画面比例
    var aspectRatio: aspectRatio?
    // 导出的项目数据
    var editProjectModel: PQEditProjectModel? {
        didSet {
            aspectRatio = PQPlayerViewModel.videoCanvasTypeToAspectRatio(projectModel: editProjectModel)
            var totalDuration: Float64 = 0
            if editProjectModel?.sData?.sections.count ?? 0 > 0 {
                for section in (editProjectModel?.sData?.sections)! {
                    totalDuration = totalDuration + section.sectionDuration
                }
            }
            editProjectModel?.sData?.videoMetaData?.duration = totalDuration
            if editProjectModel?.sData?.sections != nil, (editProjectModel?.sData?.sections.count ?? 0) > 0 {
                // 查找出背景图并设置
                var coverImageMaterialsModel: PQEditVisionTrackMaterialsModel?
                for section in (editProjectModel?.sData?.sections)! {
                    if coverImageMaterialsModel != nil {
                        break
                    }
                    coverImageMaterialsModel = section.sectionTimeline?.visionTrack?.getEnableVisionTrackMaterials().first
                }
                if coverImageMaterialsModel != nil {
                    coverImage = coverImageMaterialsModel?.getCoverImage()
                    playerHeaderView.image = coverImage
                    playerHeaderView.contentMode = coverImageMaterialsModel!.canvasFillType == stickerContentMode.aspectFitStr.rawValue ? .scaleAspectFill : .scaleAspectFit
                }
            }
        }
    }

    /// 所有需要导出的filter
    var filters: Array = Array<ImageProcessingOperation>.init()
    /// 预览背景页
    lazy var bgTopView: UIView = {
        let bgTopView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: maxHeight))
        bgTopView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
        return bgTopView
    }()

    // 预览界面
    var playerHeaderView: UIImageView = {
        let playerHeaderView = UIImageView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: 0))
        playerHeaderView.isUserInteractionEnabled = true
        playerHeaderView.contentMode = .scaleAspectFit
        playerHeaderView.clipsToBounds = true
        return playerHeaderView
    }()

    // add by ak 播放器的封面 为了不和原有的播放器层级单独添加一个 view
    lazy var playerHeaderCoverImageView: UIImageView = {
        let playerHeaderCoverImageView = UIImageView()
        playerHeaderCoverImageView.isUserInteractionEnabled = true
        playerHeaderCoverImageView.contentMode = .scaleAspectFit
        playerHeaderCoverImageView.clipsToBounds = true

        let playBtn = UIButton(type: .custom)
        playBtn.setImage(UIImage.moduleImage(named: "icon_video_play", moduleName: "BFFramework",isAssets: false), for: .normal)
        playBtn.tag = 4
        playBtn.isUserInteractionEnabled = false
        playerHeaderCoverImageView.addSubview(playBtn)
        playerHeaderCoverImageView.isHidden = true
        return playerHeaderCoverImageView
    }()

    /// 播放器
    lazy var avPlayer: AVPlayer = {
        let avPlayer = AVPlayer()
        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avPlayer.currentItem, queue: .main) { [weak self] notify in
            BFLog(message: "AVPlayerItemDidPlayToEndTime = \(notify)")
            avPlayer.seek(to: CMTime.zero)
            if self?.playerHeaderCoverImageView.image != nil {
                self?.playerHeaderCoverImageView.isHidden = false
            }
            self?.playBtn.isHidden = false
        }
        NotificationCenter.default.addObserver(forName: .AVPlayerItemNewErrorLogEntry, object: avPlayer.currentItem, queue: .main) { notify in
            BFLog(message: "AVPlayerItemNewErrorLogEntry = \(notify)")
        }
        NotificationCenter.default.addObserver(forName: .AVPlayerItemFailedToPlayToEndTime, object: avPlayer.currentItem, queue: .main) { notify in
            BFLog(message: "AVPlayerItemFailedToPlayToEndTime = \(notify)")
        }
        NotificationCenter.default.addObserver(forName: .AVPlayerItemPlaybackStalled, object: avPlayer.currentItem, queue: .main) { notify in
            BFLog(message: "AVPlayerItemPlaybackStalled = \(notify)")
        }
        avPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 1000), queue: .main) { [weak self] _ in
            let progress = CMTimeGetSeconds(avPlayer.currentItem?.currentTime() ?? CMTime.zero) / CMTimeGetSeconds(avPlayer.currentItem?.duration ?? CMTime.zero)
            if progress >= 1 {
                self?.playBtn.isHidden = false
            }
        }
        return avPlayer
    }()

    /// 预览layer
    lazy var playerLayer: AVPlayerLayer = {
        let playerLayer = AVPlayerLayer(player: avPlayer)
        playerLayer.frame = playerHeaderView.bounds
        return playerLayer
    }()

    /// 播放按钮
    lazy var playBtn: UIButton = {
        let playBtn = UIButton(type: .custom)
        playBtn.frame = CGRect(x: (preViewSize.width - cDefaultMargin * 5) / 2, y: (preViewSize.height - cDefaultMargin * 5) / 2, width: cDefaultMargin * 5, height: cDefaultMargin * 5)
        playBtn.setImage(UIImage.moduleImage(named: "icon_video_play", moduleName: "BFFramework",isAssets: false), for: .normal)
        playBtn.tag = 4
        playBtn.isHidden = true
        playBtn.isUserInteractionEnabled = false
        return playBtn
    }()

    // progressTipsLab
    lazy var progressTipsLab: UILabel = {
        let progressTipsLab = UILabel()
        progressTipsLab.textAlignment = .center
        progressTipsLab.font = UIFont.systemFont(ofSize: 14, weight: .medium)
        progressTipsLab.numberOfLines = 2
        progressTipsLab.textColor = UIColor.white
        let attributedText = NSMutableAttributedString(string: "0%\n视频正在处理中，请勿离开")
        attributedText.addAttributes([.font: UIFont.systemFont(ofSize: 34)], range: NSRange(location: 0, length: 2))
        progressTipsLab.attributedText = attributedText
        progressTipsLab.addShadow()
        return progressTipsLab
    }()

    // 进度条
    lazy var progressView: UIProgressView = {
        let progressView = UIProgressView(progressViewStyle: .default)
        progressView.trackTintColor = UIColor(white: 0, alpha: 0.5)
        progressView.progressTintColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
        progressView.transform = CGAffineTransform(scaleX: 1.0, y: playerHeaderView.frame.height)
        return progressView
    }()

    // 提示
    lazy var remindLab: UILabel = {
        let remindLab = UILabel()
        remindLab.font = UIFont.boldSystemFont(ofSize: 18)
        remindLab.textColor = PQBFConfig.shared.styleTitleColor
        remindLab.textAlignment = .center
        remindLab.numberOfLines = 2
        remindLab.backgroundColor = .clear
        remindLab.text = "为你的大作起个响亮的标题\n分享秀一下🎉"
        return remindLab
    }()

    // 输入框背景
    lazy var inputBackView: UIView = {
        let inputBackView = UIView()
        inputBackView.backgroundColor = .clear
        inputBackView.layer.cornerRadius = 7
        inputBackView.layer.borderWidth = 2
        inputBackView.layer.borderColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue).cgColor
        return inputBackView
    }()

    // 手势提示
    lazy var pinView: UIImageView = {
        let pinView = UIImageView()
        pinView.kf.setImage(with: URL(fileURLWithPath: (currentBundlePath()!.path(forResource: "editCoverPin", ofType: ".gif")!)))
        return pinView
    }()

    // 封面
    lazy var coverImageView: UIImageView = {
        let coverImageView = UIImageView()
        coverImageView.isUserInteractionEnabled = true
        coverImageView.backgroundColor = .clear
        coverImageView.contentMode = .scaleToFill
        return coverImageView
    }()

    // 封面标题
    lazy var coverImageTitle: UILabel = {
        let coverImageTitle = UILabel()
        coverImageTitle.text = "换封面"
        coverImageTitle.textAlignment = .center
        coverImageTitle.backgroundColor = UIColor(red: 0.22, green: 0.26, blue: 0.35, alpha: 0.5)
        coverImageTitle.isUserInteractionEnabled = true
        coverImageTitle.textColor = .white
        coverImageTitle.font = UIFont.boldSystemFont(ofSize: 12)
        return coverImageTitle

    }()

    // 标题
    lazy var titleLabel: UILabel = {
        let titleLabel = UILabel()
        titleLabel.numberOfLines = 2
        titleLabel.isUserInteractionEnabled = true

        titleLabel.textColor = PQBFConfig.shared.styleTitleColor
        titleLabel.textAlignment = .left
        titleLabel.font = UIFont.systemFont(ofSize: 17)
        let ges = UITapGestureRecognizer(target: self, action: #selector(titleLabelClick))
        titleLabel.addGestureRecognizer(ges)
        return titleLabel
    }()

    // 编辑发布标题
    lazy var publicTitleView: PQEditPublicTitleView = {
        let publicTitleView = PQEditPublicTitleView()
        publicTitleView.isHidden = true
        publicTitleView.confirmBtnClock = { [weak self] title in
            BFLog(message: "传出的 title  is :\(String(describing: title))")
            if title?.count != 0 && title != self?.titleLabel.text {
                self?.changPlayerIsPause(isPause: false)

                // 判断文字是否有效
                var inputText = ""
                inputText = title?.replacingOccurrences(of: "\n", with: "") ?? ""
                inputText = inputText.replacingOccurrences(of: " ", with: "")

                if inputText.count > 0 {
                    self?.setTitleText(text: title ?? "", textColor: PQBFConfig.shared.styleTitleColor)
                    // 更新数据
                    self?.videoData?.title = title
                    self?.updateCoverImagegOrTitle()
                }
            }
        }
        publicTitleView.viewIsHiddenCallBack = { [weak self] in

            self?.changPlayerIsPause(isPause: false)
        }

        return publicTitleView
    }()

    // 编辑发布封面
    lazy var publicEditCoverView: PQEditPublicCoverImageView = {
        let publicEditCoverView = PQEditPublicCoverImageView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
        publicEditCoverView.isHidden = true
        return publicEditCoverView
    }()

    // 分享到朋友圈
    lazy var shareWechatBtn: UIButton = {
        let shareWechatBtn = UIButton(type: .custom)
        shareWechatBtn.frame = CGRect(x: 0, y: 0, width: 70, height: 70)
        shareWechatBtn.setImage(UIImage.moduleImage(named: "reCreate_opration_wechat", moduleName: "BFFramework",isAssets: false), for: .normal)
        shareWechatBtn.backgroundColor = PQBFConfig.shared.styleBackGroundColor
        shareWechatBtn.addCorner(corner: 6)
        shareWechatBtn.tag = 2
        shareWechatBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
        return shareWechatBtn
    }()

    // 分享到好友
    lazy var shareFriendBtn: UIButton = {
        let shareFriendBtn = UIButton(type: .custom)
        shareFriendBtn.frame = CGRect(x: 0, y: 0, width: 70, height: 70)
        shareFriendBtn.setImage(UIImage.moduleImage(named: PQBFConfig.shared.shareFriendBtnImage, moduleName: "BFFramework",isAssets: false), for: .normal)
        shareFriendBtn.addCorner(corner: 6)
        shareFriendBtn.tag = 1
        shareFriendBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
        return shareFriendBtn
    }()

    // 关闭
    lazy var finishedBtn: UIButton = {
        let finishedBtn = UIButton(type: .custom)
        finishedBtn.setTitle("完成", for: .normal)
        finishedBtn.setTitleColor(UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue), for: .normal)
        finishedBtn.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
        finishedBtn.backgroundColor = .clear
        finishedBtn.tag = 3
        finishedBtn.addCorner(corner: 3)
        finishedBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
        return finishedBtn

    }()

    /// 背景View
    lazy var oprationBgView: UIView = {
        let oprationBgView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: view.frame.height - cDevice_iPhoneNavBarAndStatusBarHei))
        oprationBgView.backgroundColor = .clear
        return oprationBgView
    }()

    // 除了播放器以外的 下半部分操作区
    lazy var bottomOprationBgView: UIView = {
        let bottomOprationBgView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei + maxHeight, width: cScreenWidth, height: bottomOprationBgViewHeight))
        bottomOprationBgView.backgroundColor = .clear
        bottomOprationBgView.isHidden = true
        return bottomOprationBgView
    }()
    
    ///保存视频到相册提示
    lazy var saveVideoTipsBgView: UIView = {
        let saveVideoTipsBgView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: 40))
        saveVideoTipsBgView.backgroundColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.4)
        saveVideoTipsBgView.isHidden = true
        saveVideoTipsBgView.alpha = 1
        return saveVideoTipsBgView
    }()
    lazy var saveVideoTipsLabel: UILabel = {
        let saveVideoTipsLabel = UILabel(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: 40))
        saveVideoTipsLabel.textColor = .white
        saveVideoTipsLabel.textAlignment = .center
        saveVideoTipsLabel.font = UIFont.boldSystemFont(ofSize: 17)
        saveVideoTipsLabel.text = "视频保存中..."
        saveVideoTipsLabel.sizeToFit()
        return saveVideoTipsLabel
    }()
    
    // 保存重试
    lazy var saveRetryBtn: UIButton = {
        let finishedBtn = UIButton(type: .custom)
        finishedBtn.setTitle("重试", for: .normal)
        finishedBtn.setTitleColor(UIColor.white, for: .normal)
        finishedBtn.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .medium)
        finishedBtn.backgroundColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
        finishedBtn.tag = 97
        finishedBtn.isHidden = true
        finishedBtn.addCorner(corner: 5)
        finishedBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
        return finishedBtn

    }()

    override func backBtnClick() {
        if isExportSuccess {
            navigationController?.popViewController(animated: true)
        } else {
            view.endEditing(true)
            let remindData = PQBaseModel()
            remindData.title = "编辑的内容，将不会被保存"
            remindView = PQRemindView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
            remindView?.isBanned = true
            remindView?.confirmBtn.setTitle("确认", for: .normal)
            remindView?.cancelBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#333333"), for: .normal)
            remindView?.confirmBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#EE0051"), for: .normal)
            UIApplication.shared.keyWindow?.addSubview(remindView!)
            remindView?.remindData = remindData
            remindView?.remindBlock = { [weak self] item, _ in
                if item.tag == 2 {
                    // 取消导出
                    if self?.exporter != nil {
                        self?.exporter.cancel()
                    }
                    self?.navigationController?.popViewController(animated: true)
                }
            }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // 注册上传成功的通知
        addNotification(self, selector: #selector(uploadSuccess(notify:)), name: cUploadSuccessKey, object: nil)
        PQNotification.addObserver(self, selector: #selector(didBecomeActiveNotification), name: UIApplication.didBecomeActiveNotification, object: nil)
        leftButton(image: nil, tintColor: PQBFConfig.shared.styleTitleColor)
        navHeadImageView?.backgroundColor = UIColor.clear
        lineView?.removeFromSuperview()
        view.addSubview(bgTopView)
        playerHeaderView.frame = CGRect(origin: CGPoint(x: (cScreenWidth - preViewSize.width) / 2, y: (maxHeight - preViewSize.height) / 2), size: preViewSize)
        let ges = UITapGestureRecognizer(target: self, action: #selector(playVideo))
        playerHeaderView.addGestureRecognizer(ges)

        if playerLayer.superlayer == nil {
            playerHeaderView.layer.insertSublayer(playerLayer, at: 0)
        }
        playerHeaderView.addSubview(playBtn)
        playerHeaderView.addSubview(progressView)
        view.addSubview(oprationBgView)
        oprationBgView.addSubview(progressTipsLab)

        // 添加导出view
        bgTopView.addSubview(playerHeaderView)

        playerHeaderCoverImageView.frame = playerHeaderView.frame
        playerHeaderCoverImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(playVideo)))

        (playerHeaderCoverImageView.viewWithTag(4))?.frame =
            CGRect(x: (preViewSize.width - cDefaultMargin * 5) / 2, y: (preViewSize.height - cDefaultMargin * 5) / 2, width: cDefaultMargin * 5, height: cDefaultMargin * 5)
        bgTopView.addSubview(playerHeaderCoverImageView)

        view.addSubview(bottomOprationBgView)
        bottomOprationBgView.addSubview(remindLab)
        bottomOprationBgView.addSubview(shareWechatBtn)
        bottomOprationBgView.addSubview(shareFriendBtn)
        bottomOprationBgView.addSubview(finishedBtn)
        bottomOprationBgView.addSubview(inputBackView)
        bottomOprationBgView.addSubview(pinView)
        inputBackView.addSubview(coverImageView)
        coverImageView.addSubview(coverImageTitle)
        inputBackView.addSubview(titleLabel)

        view.addSubview(publicTitleView)
        view.addSubview(publicEditCoverView)
        
        
        view.addSubview(saveVideoTipsBgView)
        saveVideoTipsBgView.addSubview(saveVideoTipsLabel)
        saveVideoTipsBgView.addSubview(saveRetryBtn)
        saveVideoTipsLabel.snp.makeConstraints { make in
            make.top.height.equalToSuperview()
            make.centerX.equalToSuperview()
        }
        saveRetryBtn.snp.makeConstraints { make in
            make.left.equalTo(saveVideoTipsLabel.snp.right).offset(10)
            make.top.equalTo(6)
            make.bottom.equalTo(-6)
            make.width.equalTo(50)
        }

 
        coverImageTitle.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(settingCoverImage)))
        coverImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(settingCoverImage)))

        progressView.snp.makeConstraints { make in
            make.left.right.centerY.equalTo(playerHeaderView)
            make.height.equalTo(3)
        }
        progressTipsLab.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalToSuperview().offset(((preViewSize.height - 90) / 2) + ((maxHeight - preViewSize.height) / 2))
            make.width.equalToSuperview()
            make.height.equalTo(90)
        }
        finishedBtn.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.bottom.equalToSuperview().offset(-cSafeAreaHeight)
            make.width.equalTo(100)
            make.height.equalTo(22)
        }
        shareWechatBtn.snp.makeConstraints { make in
            make.right.equalTo(view.snp.centerX).offset(-cDefaultMargin)
            make.width.equalTo(164)
            make.height.equalTo(52)
            make.bottom.equalTo(finishedBtn.snp.top).offset(-32)
        }
        shareFriendBtn.snp.makeConstraints { make in
            make.left.equalTo(view.snp.centerX).offset(cDefaultMargin)
            make.width.bottom.height.equalTo(shareWechatBtn)
        }

        inputBackView.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.bottom.equalTo(shareWechatBtn.snp.top).offset(-16)
            make.width.equalTo(343)
            make.height.equalTo(109)
        }

        // 根据横竖屏设置不同的 UI
        let isWidth: Bool = (Float(editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) / Float(editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)) >= 1
        var coverImageViewHeight = 50.0 * Float(editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) / Float(editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)
        if coverImageViewHeight > 89 {
            coverImageViewHeight = 89
        }

        coverImageView.snp.makeConstraints { make in
            make.left.equalToSuperview().offset(12)
            make.width.equalTo(50)
            make.top.equalToSuperview().offset(10)
            make.height.equalTo(coverImageViewHeight)
        }

        coverImageTitle.snp.makeConstraints { make in
            make.left.equalToSuperview()
            make.width.equalTo(50)
            make.top.equalTo(coverImageView.snp.bottom).offset(isWidth ? 0 : -23)
            make.height.equalTo(23)
        }

        remindLab.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.bottom.equalTo(inputBackView.snp.top).offset(-16).priorityHigh()
            make.height.equalTo(44)
            make.top.greaterThanOrEqualTo(5)
            make.bottom.lessThanOrEqualTo(inputBackView.snp.top).offset(-5)
        }

        titleLabel.snp.makeConstraints { make in
            make.height.equalTo(48)
            make.left.equalTo(coverImageView.snp.right).offset(12)
            make.right.equalToSuperview().offset(-14)
            make.top.equalToSuperview().offset(10)
        }

        pinView.snp.makeConstraints { make in
            make.height.width.equalTo(72)
            make.right.equalToSuperview()
            make.bottom.equalTo(inputBackView.snp.bottom)
        }

        publicTitleView.snp.makeConstraints { make in
            make.height.equalTo(cScreenHeigth)
            make.width.equalTo(cScreenWidth)
            make.bottom.equalToSuperview()
        }

        // 取消所有的导出
        PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
            exportSession.cancelExport()
        }
        // 开始导出
        appendAudio()
        /// 保存草稿
        saveDraftbox()
        // 曝光上报：窗口曝光
        PQEventTrackViewModel.baseReportUpload(businessType: .bt_windowView, objectType: .ot_view_publishSyncedUp, pageSource: .sp_stuck_publishSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-（曝光上报：窗口曝光）")

        // 取推荐标题
        getTitles()
        
        networkStausListen()
      
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        PQNotification.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
        PQNotification.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
        
        DispatchQueue.main.async {
            UIApplication.shared.isIdleTimerDisabled = true
        }
        //从相册选择一个照片后回调
        addNotification(self, selector: #selector(imageSelectedImage(notify:)), name: cSelectedImageSuccessKey, object: nil)

        #if swift(>=4.2)
            let memoryNotification = UIApplication.didReceiveMemoryWarningNotification
            _ = UIApplication.willTerminateNotification
            _ = UIApplication.didEnterBackgroundNotification
        #else
            let memoryNotification = NSNotification.Name.UIApplicationDidReceiveMemoryWarning
            let terminateNotification = NSNotification.Name.UIApplicationWillTerminate
            let enterbackgroundNotification = NSNotification.Name.UIApplicationDidEnterBackground
        #endif

        NotificationCenter.default.addObserver(
            self, selector: #selector(clearMemoryCache), name: memoryNotification, object: nil
        )
    }

    @objc public func clearMemoryCache() {
        BFLog(message: "收到内存警告")
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        DispatchQueue.main.async {
            UIApplication.shared.isIdleTimerDisabled = false
        }
    }

    deinit {
        BFLog(1, message: "发布界面析构release")
        view.endEditing(true)
        PQNotification.removeObserver(self)
        // 取消导出
        if exporter != nil {
            exporter.cancel()
        }
        if watermarkMovieExporter != nil{
            watermarkMovieExporter.cancel()
        }
        if endMovieExporter != nil{
            endMovieExporter.cancel()
        }
 
        exporter?.input?.removeAllTargets()
        watermarkMovieExporter?.input?.removeAllTargets()
        endMovieExporter?.input?.removeAllTargets()
        avPlayer.pause()
        avPlayer.replaceCurrentItem(with: nil)
        // 点击上报：返回按钮
        PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_back, pageSource: .sp_stuck_publishSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-（点击上报：返回按钮）")
    }
    // MARK: - 网络监控
    func networkStausListen(){
        manager?.startListening(onUpdatePerforming: { status in
            if status == .reachable(.cellular) || status == .reachable(.ethernetOrWiFi) {
                cHiddenHUB(superView: nil)
            } else {
                cShowHUB(superView: nil, msg: "当前网络不佳，请尝试重新连接")
            }
        })
    }
}


// MARK: - 导出/上传/下载及其他方法

/// 导出/上传/下载及其他方法
extension PQStuckPointPublicController {
    /// fp1 - 导出视频
    /// 开始导出视频

    /// 合并声音
    /// - Parameter urls: 所有音频的URL  是全路径方便复用
    /// - Parameter completeHander: 返回的 URL 全路径的 URL 如果要保存替换掉前缀
    func mergeAudios(originAsset: AVURLAsset, mTotalDuration: Float, clipAudioRange: CMTimeRange = CMTimeRange.zero, mStartTime _: CMTime = .zero, completeHander: @escaping (_ fileURL: URL?) -> Void) {
        let timeInterval: TimeInterval = Date().timeIntervalSince1970
        let composition = AVMutableComposition()

        let originaDuration = CMTimeGetSeconds(clipAudioRange.duration)
        BFLog(message: "处理主音频 原始时长startTime = \(originaDuration) 要显示时长totalDuration = \(mTotalDuration)")
        //originaDuration =  37.616768 mTotalDuration = 37.616776 TODO 都用 INT 微秒级
        if  Float64(String(format: "%.3f",mTotalDuration)) ?? 0.0 <=  Float64(String(format: "%.3f",originaDuration)) ?? 0.0 {
            BFLog(message: "不用拼接音频文件 \(originAsset.url) 时长is \(CMTimeGetSeconds(originAsset.duration))")
            completeHander(originAsset.url)
            return
        }

        // 整倍数
        let count = Int(mTotalDuration) / Int(originaDuration)

        // 有余数多 clip 一整段
        let row = mTotalDuration - Float(count) * Float(originaDuration)
        // 已经拼接的总时长
        var totalDuration: CMTime = .zero
        // 第一段的时长
        var duration: CMTime = .zero
        // 第一段的区间
        var timeRange: CMTimeRange = CMTimeRange.zero
        if count > 0 {
            for index in 0 ..< count {
                // 第0段从0开始到推荐的结束，播放器的开始时间不是从0开始的
                duration = CMTime(value: CMTimeValue((CMTimeGetSeconds(clipAudioRange.end)) * Double(playerTimescaleInt)), timescale: playerTimescaleInt)
                BFLog(message: "每一个文件的 duration \(CMTimeGetSeconds(duration))")
                var timeRange = CMTimeRangeMake(start: .zero, duration: duration)

                if index != 0 {
                    // (CMTimeGetSeconds(clipAudioRange.end) - CMTimeGetSeconds(mStartTime))为用户选择的第一段时长
                    timeRange = clipAudioRange
                }

                BFLog(message: "合并的文件地址: \(originAsset.url)")
                let audioAsset = originAsset
                let tracks = audioAsset.tracks(withMediaType: .audio)
                if tracks.count == 0 {
                    BFLog(message: "音频数据无效不进行合并,所有任务结束要确保输入的数据都正常！ \(originAsset.url)")
                    completeHander(URL(string: ""))
                    return
                }
                let assetTrack: AVAssetTrack = tracks[0]

                let compositionAudioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID())!

                do {
                    //
                    try compositionAudioTrack.insertTimeRange(timeRange, of: assetTrack, at: totalDuration)

                } catch {
                    BFLog(message: "error is \(error)")
                    completeHander(URL(string: ""))
                    return
                }
                totalDuration = CMTimeAdd(totalDuration, timeRange.duration)
            }
        }

        if row > 0 {
            duration = CMTime(value: CMTimeValue(Float(CMTimeGetSeconds(totalDuration)) * Float(playerTimescaleInt)), timescale: playerTimescaleInt)
            timeRange = CMTimeRange(start: clipAudioRange.start, duration: CMTime(value: Int64(Double(row) * Double(playerTimescaleInt)), timescale: playerTimescaleInt))

            BFLog(message: "合并的文件地址: \(originAsset.url)")
            let audioAsset = originAsset
            let tracks = audioAsset.tracks(withMediaType: .audio)
            if tracks.count == 0 {
                BFLog(message: "音频数据无效不进行合并,所有任务结束要确保输入的数据都正常！ \(originAsset.url)")
                completeHander(URL(string: ""))
                return
            }
            let assetTrack: AVAssetTrack = tracks[0]

            let compositionAudioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID())!

            do {
                //
                try compositionAudioTrack.insertTimeRange(timeRange, of: assetTrack, at: totalDuration)

            } catch {
                BFLog(message: "合并音频 error is \(error)")
                completeHander(URL(string: ""))
                return
            }
            totalDuration = CMTimeAdd(totalDuration, timeRange.duration)
        }

        let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)
        BFLog(message: "assetExport.supportedFileTypes is \(String(describing: assetExport?.supportedFileTypes))")

        assetExport?.outputFileType = .m4a
        // XXXX 注意文件名的后缀要和outputFileType 一致 否则会导出失败
        var audioFilePath = exportAudiosDirectory

        if !directoryIsExists(dicPath: audioFilePath) {
            BFLog(message: "文件夹不存在")
            createDirectory(path: audioFilePath)
        }
        audioFilePath.append("merge_\(timeInterval).m4a")

        let fileUrl = URL(fileURLWithPath: audioFilePath)

        assetExport?.outputURL = fileUrl
        assetExport?.exportAsynchronously {
            if assetExport!.status == .completed {
                let audioAsset = AVURLAsset(url: fileUrl, options: avAssertOptions)
                BFLog(1, message: "拼接声音文件 完成 \(fileUrl) 时长is \(CMTimeGetSeconds(audioAsset.duration))")
                completeHander(fileUrl)

            } else {
                print("拼接出错 \(String(describing: assetExport?.error))")
                completeHander(URL(string: ""))
            }
        }
    }

    func appendAudio() {
        //更新一下假进度
        updatePublicCurrentProgress(useProgress: 0.01)
        let inputAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + (audioMixModel?.localPath ?? "")), options: nil)
        let startMergeTime = CFAbsoluteTimeGetCurrent()
        mergeAudios(originAsset: inputAsset, mTotalDuration: finallyUserAudioTime, clipAudioRange: clipAudioRange, mStartTime: CMTime(value: CMTimeValue((mStickers?.first?.timelineIn ?? 0.0) * Float64(playerTimescaleInt)), timescale: playerTimescaleInt)) { [weak self] completURL in

            if completURL != nil {
                let asset = AVURLAsset(url: completURL!, options: nil)
                BFLog(message: "拼接后音频时长\(asset.duration.seconds)  url is \(String(describing: completURL)) 用时\(CFAbsoluteTimeGetCurrent() - startMergeTime)")
                //导出不带水印的正片
                self?.beginExport(inputAsset: asset)
                //导出带水印的正片
                self?.beginExportWatermarkMovie(inputAsset:asset)
            }else{
                cShowHUB(superView: self?.view, msg: "合成失败请重试。")
            }
        }
    }

    func beginExport(inputAsset: AVURLAsset!) {
        if !(editProjectModel?.sData?.sections != nil && (editProjectModel?.sData?.sections.count ?? 0) > 0) {
            BFLog(message: "项目段落错误❌")
            return
        }
        // 输出视频地址
        var outPutMP4Path = exportVideosDirectory
        if !directoryIsExists(dicPath: outPutMP4Path) {
            BFLog(message: "文件夹不存在")
            createDirectory(path: outPutMP4Path)
        }
        outPutMP4Path.append("video_\(String.qe.timestamp()).mp4")
        let outPutMP4URL = URL(fileURLWithPath: outPutMP4Path)
        BFLog(message: "导出视频地址 \(outPutMP4URL)")

        exporter = PQCompositionExporter(asset: inputAsset, videoComposition: nil, audioMix: nil, filters: nil, stickers: mStickers, animationTool: nil, exportURL: outPutMP4URL)

        var orgeBitRate = (editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * (editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 3

        if mStickers != nil {
            for stick in mStickers! {
                if stick.type == StickerType.VIDEO.rawValue {
                    let asset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + stick.locationPath), options: avAssertOptions)

                    let cbr = asset.tracks(withMediaType: .video).first?.estimatedDataRate
                    if Int(cbr ?? 0) > orgeBitRate {
                        orgeBitRate = Int(cbr ?? 0)
                    }
                }
            }
        }
        BFLog(message: "导出设置的码率为:\(orgeBitRate)")
        exporter.showGaussianBlur = true
        if exporter.prepare(videoSize: CGSize(width: editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0), videoAverageBitRate: orgeBitRate) {
            BFLog(message: "开始导出 \(String(describing: playeTimeRange.start)) 结束 \(String(describing: playeTimeRange.end))")
            exporter.start(playeTimeRange: playeTimeRange)
            BFLog(message: "开始导出")
        }
        exporter.progressClosure = { [weak self] _, _, progress in
            BFLog(message: "正片合成进度 \(progress)")
            let useProgress = progress > 1 ? 1 : progress
            if progress > 0, Int(useProgress * 100) > (self?.exportProgrss ?? 0) {
                // 更新进度
                self?.updatePublicCurrentProgress(useProgress: useProgress * 0.88)
            }
        }
        exporter.completion = { [weak self] url in
            BFLog(message: "无水印的视频导出完成: \(String(describing: url)) 生成视频时长为：\(CMTimeGetSeconds(AVAsset(url: (url ?? URL(string: "https://media.w3.org/2010/05/sintel/trailer.mp4")!)).duration))")

            // 导出完成后取消导出
            if self?.exporter != nil {
                self?.exporter.cancel()
            }
            self?.remindView?.removeFromSuperview()
            if !(self?.isExportSuccess ?? false) {
                self?.isExportSuccess = true
                self?.exportEndDate = Date().timeIntervalSince1970
                BFLog(message: "视频导出完成-开始去发布视频 总时长为\((self?.exportEndDate ?? 0) - (self?.startExportDate ?? 0) * 1000)")

                self?.exportLocalURL = url
                /// fp2-1-1 - 请求权限
//                self?.authorizationStatus()
                /// fp2-2 - 保存草稿
//                self?.saveDraftbox()
                /// fp2 - 处理视频数据
                self?.dealWithVideoData()
            }
        }
    }

    /// fp2-1-1 - 请求权限
    func authorizationStatus() {
        let authStatus = PHPhotoLibrary.authorizationStatus()
        if authStatus == .notDetermined {
            // 第一次触发授权 alert
            PHPhotoLibrary.requestAuthorization { [weak self] (status: PHAuthorizationStatus) -> Void in
                if status != .authorized {
                    cShowHUB(superView: nil, msg: "您尚未打开相册权限，请到设置页打开相册权限")
                } else {
                    /// fp2-1-2 - 保存视频到相册
                    self?.saveStuckPointVideo()
                }
            }
        } else if authStatus == .authorized {
            /// fp2-1-2 - 保存视频到相册
            saveStuckPointVideo()
        } else {
            cShowHUB(superView: nil, msg: "您尚未打开相册权限，请到设置页打开相册权限")
        }
    }

    /// fp2-1-2 - 保存视频到相册
    /// - Parameter localPath: localPath description
    /// - Returns: <#description#>
    func saveStuckPointVideo() {
        
        if(saveMovieLocalURL == nil){
            BFLog(message: "保存相册的视频导出地址无效！！！")
            cShowHUB(superView: nil, msg: "保存相册的视频导出地址无效")
            saveVideoTipsLabel.text = "视频保存失败"
            saveRetryBtn.isHidden = false
            saveVideoTipsBgView.isHidden = false
//            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.8) { [weak self] in
//                self?.saveVideoTipsBgView.isHidden = true
//            }
            return
        }
        let authStatus = PHPhotoLibrary.authorizationStatus()
        if authStatus == .authorized {
            let photoLibrary = PHPhotoLibrary.shared()
            photoLibrary.performChanges({ [weak self] in
                self?.isSaveingLocalVideo = true
                PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: (self?.saveMovieLocalURL)!)
            }) { [weak self] isFinished, _ in
                self?.isSaveingLocalVideo = false
                DispatchQueue.main.async { [weak self] in
                    if self?.view != nil {
                        if isFinished {
                            self?.saveVideoTipsLabel.text = "视频已保存到相册"
                            self?.saveRetryBtn.isHidden = true
                            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.8) { [weak self] in
                                self?.saveVideoTipsBgView.isHidden = true
                            }
                         
                        } else {
                            self?.saveVideoTipsLabel.text = "视频保存失败"
                            self?.saveRetryBtn.isHidden = false
//                            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.8) { [weak self] in
//                                self?.saveVideoTipsBgView.isHidden = true
//                            }

                            
                        }
                    }
                }
            }
        } else {
            cShowHUB(superView: nil, msg: "您尚未打开相册权限，请到设置页打开相册权限")
        }
    }

    /// fp2-2 - 保存草稿
    /// - Returns: <#description#>
    @objc func saveDraftbox() {
        let sdata = editProjectModel?.sData?.toJSONString(prettyPrint: false)
        if sdata != nil, (sdata?.count ?? 0) > 0 {
            DispatchQueue.global().async { [weak self] in
                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
                    if draftboxInfo != nil {
                        self?.editProjectModel?.draftboxId = draftboxInfo?["draftboxId"] as? String ?? ""
                        self?.editProjectModel?.sData?.videoMetaData?.title = draftboxInfo?["title"] as? String ?? ""
                        self?.editProjectModel?.sData?.videoMetaData?.coverUrl = draftboxInfo?["coverUrl"] as? String ?? ""
                        self?.editProjectModel?.dataVersionCode = draftboxInfo?["dataVersionCode"] as? Int ?? 0
                        BFLog(message: "保存远程的草稿成功")
                        self?.isSaveDraftSuccess = true
                        /// fp3 - 保存项目
                        self?.saveProject()
                    } else {
                        // 保存草稿失败-播放视频
//                        self?.publicEnd(isError: true)
                    }
                }
            }
        } else {
            cShowHUB(superView: nil, msg: "您尚未打开相册权限，请到设置页打开相册权限")
            // 保存草稿失败-播放视频
            publicEnd(isError: true)
        }
    }

    /// fp3 - 保存项目
    /// - Returns: description
    func saveProject() {
        if isSaveDraftSuccess {
            let sdata = editProjectModel?.sData?.toJSONString(prettyPrint: false) ?? ""
            let draftboxId: String? = editProjectModel?.draftboxId
            PQBaseViewModel.saveProject(draftboxId: draftboxId, sdata: sdata, videoFromScene: .stuckPoint, rhythmMode: rhythmMode) { [weak self] projectId, msg in
                BFLog(message: "生成的项目id1111 ：\(projectId ?? ""),msg = \(msg ?? "")")
                if projectId == nil || (projectId?.count ?? 0) <= 0 {
                    PQBaseViewModel.saveProject(draftboxId: draftboxId, sdata: sdata, videoFromScene: .stuckPoint, rhythmMode: self?.rhythmMode ?? .createStickersModelPoint) { [weak self] projectId, msg in
                        BFLog(message: "生成的项目id222 ：\(projectId ?? ""),msg = \(msg ?? "")")
                        if projectId == nil || (projectId?.count ?? 0) <= 0 {
                            PQBaseViewModel.saveProject(draftboxId: draftboxId, sdata: sdata, videoFromScene: .stuckPoint, rhythmMode: self?.rhythmMode ?? .createStickersModelPoint) { [weak self] projectId, msg in
                                BFLog(message: "生成的项目id 3333：\(projectId ?? ""),msg = \(msg ?? "")")
                                if projectId != nil, (projectId?.count ?? 0) > 0 {
                                    self?.editProjectModel?.projectId = projectId ?? ""
                                }
                                /// fp4 - 处理视频数据
//                                self?.dealWithVideoData()
                            }
                        } else {
                            self?.editProjectModel?.projectId = projectId ?? ""
                            /// fp4 - 处理视频数据
//                            self?.dealWithVideoData()
                        }
                    }
                } else {
                    self?.editProjectModel?.projectId = projectId ?? ""
                    /// fp4 - 处理视频数据
//                    self?.dealWithVideoData()
                }
            }
        }
    }

    /// fp4 - 处理视频数据
    /// - Returns: description
    @objc func dealWithVideoData() {
        BFLog(message: "开始去发布视频12")
        isSaveProjectSuccess = true
        if isExportSuccess && exportLocalURL != nil {
            BFLog(message: "素材上传完成同时视频导出完成开始发布视频")
            // 更新项目
            PQBaseViewModel.updateProject(projectId: editProjectModel?.projectId ?? "", produceStatus: "5") { repseon, _ in
                BFLog(message: "updateProject 结果 is \(String(describing: repseon))")
            }
            let asset = AVURLAsset(url: exportLocalURL!, options: nil)
            let tempUploadData = PQUploadModel()
            tempUploadData.duration = CMTimeGetSeconds(asset.duration)
            tempUploadData.localPath = exportLocalURL?.absoluteString
            tempUploadData.videoWidth = CGFloat(editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)
            tempUploadData.videoHeight = CGFloat(editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)
            tempUploadData.image = PQVideoSnapshotUtil.videoSnapshot(videoURL: exportLocalURL!, time: 0)
            if tempUploadData.image == nil {
                tempUploadData.image = coverImage
            }
            tempUploadData.videoFromScene = .stuckPoint
            eventTrackData = getExportEventTrackData()
            eventTrackData?.projectId = editProjectModel?.projectId ?? ""
            uploadData = tempUploadData
            if uploadData?.image == nil {
                uploadData?.image = PQVideoSnapshotUtil.videoSnapshot(videoURL: exportLocalURL!, time: 0)
            }
            if uploadData?.image != nil {
                playerHeaderView.image = uploadData?.image
                coverImageView.image = uploadData?.image
            }
            if isExportSuccess, exportLocalURL != nil {
                let size = try! exportLocalURL?.resourceValues(forKeys: [.fileSizeKey])
                BFLog(message: "size = \(String(describing: size))")
                if Float64(size?.fileSize ?? 0) <= maxUploadSize {
                    /// fp5 - 上传视频
                    reUploadVideo()
                }
            }
        }
    }

    /// fp5 - 上传视频
    /// - Returns: <#description#>
    @objc func reUploadVideo() {
        if uploadData?.stsToken != nil {
            multipartUpload(response: uploadData?.stsToken)
        } else {
            uploadVideo()
        }
    }

    /// fp5-1 - 开始上传视频
    /// - Returns: <#description#>
    func uploadVideo() {
        let uploadRequest: OSSMultipartUploadRequest? = PQAliOssUtil.shared.allTasks[uploadData?.videoBucketKey ?? ""]
        if uploadRequest != nil, "\(uploadRequest?.callbackParam["code"] ?? "0")" == "1" {
            return
        }
        // 更新进度
        updatePublicCurrentProgress(useProgress: 0.89)
        DispatchQueue.global().async {
            PQBaseViewModel.getStsToken { [weak self] response, _ in
                if response == nil {
                    self?.showUploadRemindView(isNetCollected: false, msg: "token获取数据失败了哦～")
                    return
                }
                // 更新进度
                self?.updatePublicCurrentProgress(useProgress: 0.90)
                BFLog(message: "取我方服务器STS 返回数据 \(String(describing: response))")
                self?.multipartUpload(response: response)
            }
        }
    }

    /// fp5-2 - 继续上传视频
    /// - Parameter response: <#response description#>
    func multipartUpload(response: [String: Any]?) {
        let FileName: String = "\(response?["FileName"] ?? "")"
        let uploadID: String = "\(response?["Upload"] ?? "")"
        uploadData?.stsToken = response
        uploadData?.videoBucketKey = FileName
        uploadData?.uploadID = uploadID
        if uploadData?.asset != nil && isValidURL(url: uploadData?.localPath) {
            PQPHAssetVideoParaseUtil.exportPHAssetToMP4(phAsset: (uploadData?.asset)!, isCancelCurrentExport: true) { [weak self] _, _, filePath, _ in
                if filePath != nil, (filePath?.count ?? 0) > 0 {
                    self?.uploadData?.localPath = filePath
                    PQAliOssUtil.multipartUpload(localPath: self?.uploadData?.localPath ?? "", response: response)
                }
            }
        } else {
            PQAliOssUtil.multipartUpload(localPath: uploadData?.localPath ?? "", response: response)
        }
        PQAliOssUtil.shared.aliOssHander = { [weak self] isMatarialUpload, materialType, _, code, objectkey, _, _, _, _, _, _, _, _, _ in
            if !isMatarialUpload, materialType == .VIDEO, self?.uploadData?.videoBucketKey == objectkey {
                if code == 6 { // 无网
                    let uploadRequest: OSSMultipartUploadRequest? = PQAliOssUtil.shared.allTasks[self?.uploadData?.videoBucketKey ?? ""]
                    if !(uploadRequest != nil && "\(uploadRequest?.callbackParam["code"] ?? "0")" == "1") {
                        self?.showUploadRemindView(msg:"aliOss")
                    }
                } else if code == 260 {
                    self?.showUploadRemindView(isNetCollected: false, msg:"aliOss")
                } else if code != 1 {
                    // 上传失败-播放视频
                    self?.publicEnd(isError: true)
                }
            }
        }

        PQAliOssUtil.shared.aliOssProgressHander = { [weak self] bytesSent, totalBytesSent, totalBytesExpectedToSend, _, _ in
            let progress: Float = 0.90 + Float(Float(totalBytesSent) / Float(totalBytesExpectedToSend)) * 0.09
            BFLog(message: "卡点视频上传：bytesSent = \(bytesSent),totalBytesSent = \(totalBytesSent),totalBytesExpectedToSend = \(totalBytesExpectedToSend),progress = \(progress)")
            if progress >= 0.90, progress <= 0.99 {
                // 更新进度
                self?.updatePublicCurrentProgress(useProgress: progress)
            }
        }
    }

    /// fp6 - 视频上传成功，处理要发布视频数据
    /// - Parameter notify: <#notify description#>
    @objc func uploadSuccess(notify: NSNotification) {
        let objectKey: String = "\(notify.userInfo?["objectKey"] ?? "")"
        BFLog(message: "收到上传成功请求==\(notify.userInfo ?? [:])")
        if uploadData?.videoBucketKey == objectKey {
            // 上传成功
            isUploadSuccess = true
            /// fp7 - 处理要发布视频数据
            dealWithPublicData()
        }
    }

    /// fp7 - 处理要发布视频数据
    /// - Returns: <#description#>
    func dealWithPublicData() {
        if uploadData?.localPath != nil {
            let size = try! URL(string: uploadData?.localPath ?? "")?.resourceValues(forKeys: [.fileSizeKey])
            BFLog(message: "size = \(String(describing: size))")
            if Float64(size?.fileSize ?? 0) > maxUploadSize {
                cShowHUB(superView: nil, msg: "无法发布大于10G的视频,请重新选择/合成发布")
                // 上传失败-播放视频
                publicEnd(isError: true)
                return
            }
        }
        let projectId: String? = editProjectModel?.projectId
        let uploadRequest: OSSMultipartUploadRequest? = PQAliOssUtil.shared.allTasks[uploadData?.videoBucketKey ?? ""]
        if uploadRequest == nil {
            reUploadVideo()
            return
        }
        let tempModel = PQVideoListModel()
        tempModel.title = selectTitle
        tempModel.summary = ""
        tempModel.duration = Float64(uploadData?.duration ?? 0)
        tempModel.uplpadImage = uploadData?.image
        tempModel.uplpadBucketKey = uploadRequest?.objectKey
        tempModel.localPath = uploadData?.localPath
        tempModel.reCreateVideoData = reCreateData
        tempModel.eventTrackData = eventTrackData
        tempModel.uplpadStatus = 1
        tempModel.videoFromScene = .stuckPoint
        tempModel.uid = Int(BFLoginUserInfo.shared.uid) ?? 0
        tempModel.uplpadRequest = PQAliOssUtil.shared.allTasks[uploadData?.videoBucketKey ?? ""]
        tempModel.stsToken = uploadData?.stsToken
        tempModel.projectId = projectId

        /// fp8 - 发布视频
        publicVideo(videoData: tempModel)
    }

    /// fp8 - 发布视频
    /// - Parameter videoData: <#videoData description#>
    func publicVideo(videoData: PQVideoListModel) {
        if videoData.uplpadBucketKey == nil {
            BFLog(message: "发布视频：视频uplpadBucketKey为空-\(String(describing: videoData.uplpadBucketKey))")
            // 上传失败-播放视频
            publicEnd(isError: true)
            return
        }
        BFLog(message: "开始发布")
        if (videoData.eventTrackData?.endUploadDate ?? 0) <= 0 {
            // 结束上传时间
            videoData.eventTrackData?.endUploadDate = Date().timeIntervalSince1970
        }
        DispatchQueue.global().async {
//            PQBaseViewModel.ossTempToken { [weak self] response, _ in
//                let image: UIImage = videoData.uplpadImage ?? UIImage()
//                let data = image.jpegData(compressionQuality: 1)
//                let accessKeyId: String = "\(response?["accessKeyId"] ?? "")"
//                let secretKeyId: String = "\(response?["accessKeySecret"] ?? "")"
//                let securityToken: String = "\(response?["securityToken"] ?? "")"
//                let endpoint: String = "\(response?["uploadDomain"] ?? "")"
//                let bucketName: String = "\(response?["bucketName"] ?? "")"
//                let objectKey: String = "\(response?["objectKey"] ?? "")"
//                BFLog(message: "开始上传视频图片==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? "")，objectKey =\(objectKey)")
//                PQAliOssUtil.shared
//                    .startClient(
//                        accessKeyId: accessKeyId,
//                        secretKeyId: secretKeyId,
//                        securityToken: securityToken,
//                        endpoint: endpoint
//                    )
//                    .uploadObjectAsync(bucketName: bucketName, objectKey: objectKey, data: data!, fileExtensions: "png", imageUploadBlock: { _, code, ossObjectKey, _ in
//                        BFLog(message: "图片上传完成==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? "")，objectKey =\(objectKey)，ossObjectKey = \(ossObjectKey)")
//                        if code == 1 && ossObjectKey == objectKey && objectKey.count > 0 {
//                            BFLog(message: "开始发布==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? "")，objectKey =\(objectKey)，ossObjectKey = \(ossObjectKey)")
            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
                self?.videoData = newVideoData
                self?.videoData?.title = self?.titleLabel.text
                if self?.videoData?.reCreateVideoData == nil {
                    let reCreateVideo = PQReCreateModel()
                    reCreateVideo.reProduceVideoFlag = 1
                    self?.videoData?.reCreateVideoData = reCreateVideo
                }
                postNotification(name: cPublishStuckPointSuccessKey, userInfo: ["newVideoData": self?.videoData ?? PQVideoListModel()])
                BFLog(message: "发布成功==\(videoData.title ?? ""),uplpadBucketKey = \(videoData.uplpadBucketKey ?? "")")
//                                cShowHUB(superView: nil, msg: "视频发布成功")
                // 发布成功后续操作
                self?.publicEnd()
                PQEventTrackViewModel.publishReportUpload(projectId: videoData.projectId, businessType: .bt_publish_success, ossInfo: videoData.stsToken ?? [:], params: ["title": videoData.title ?? "", "videoPath": videoData.uplpadBucketKey ?? "", "descr": videoData.summary ?? ""])
            }
//                        } else {
//                            // 图片上传失败
//                            BFLog(message: "图片上传失败重新发布视频==\(videoData.title ?? ""),\(videoData.uplpadBucketKey ?? "")")
//                            self?.publicVideo(videoData: videoData)
//                        }
//                    })
//            }
        }
    }

    /// 发布结束操作
    /// - Parameter isError: <#isError description#>
    /// - Returns: <#description#>
    func publicEnd(isError: Bool = false) {
        UIApplication.shared.keyWindow?.viewWithTag(100_100)?.removeFromSuperview()
        isPublicSuccess = true
        progressView.removeFromSuperview()
        progressTipsLab.removeFromSuperview()
        oprationBgView.removeFromSuperview()
        playBtn.isHidden = true
        avPlayer.replaceCurrentItem(with: AVPlayerItem(url: URL(fileURLWithPath: (exportLocalURL?.absoluteString ?? "").replacingOccurrences(of: "file:///", with: ""))))
        avPlayer.play()
        if isError {
            cShowHUB(superView: nil, msg: "视频发布失败，请重新合成")
        } else {
            bottomOprationBgView.isHidden = false
            //add by ak 发布成功后如果带片尾的视频还没有生成成功时，出提示
            self.saveRetryBtn.isHidden = true
            if(saveMovieLocalURL == nil && self.isSaveingLocalVideo){
                saveVideoTipsLabel.text = "视频保存中..."
            }else{
                self.saveVideoTipsLabel.text = "视频已保存到相册"
                DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.8) { [weak self] in
                    self?.saveVideoTipsBgView.isHidden = true
                }
            }
            if self.isSaveingLocalVideo{
                saveVideoTipsBgView.isHidden = false
            }
            
        }
    }

    /// 生成创作工具埋点数据
    /// - Returns: <#description#>
    func getExportEventTrackData() -> PQVideoMakeEventTrackModel? {
        let eventTrackData = PQVideoMakeEventTrackModel(projectModel: editProjectModel, reCreateData: reCreateData)
        eventTrackData.entrance = .entranceStuckPointPublic
        eventTrackData.editTimeCost = 0
        eventTrackData.composeTimeCost = (exportEndDate - startExportDate) * 1000
        eventTrackData.musicName = audioMixModel?.musicName ?? ""
        eventTrackData.syncedUpMusicName = audioMixModel?.musicName ?? ""
        eventTrackData.musicId = audioMixModel?.musicId ?? ""
        eventTrackData.syncedUpMusicId = audioMixModel?.musicId ?? ""
        eventTrackData.musicUrl = audioMixModel?.selectVoiceType == 1 ? (audioMixModel?.musicPath ?? "") : (audioMixModel?.accompanimentPath ?? "")
        eventTrackData.musicType = audioMixModel != nil ? (audioMixModel?.selectVoiceType == 1 ? "original" : "accompaniment") : ""
        eventTrackData.isMusicClip = (audioMixModel?.startTime ?? 0) > 0
        if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.origin.rawValue {
            eventTrackData.canvasRatio = "original"
        } else if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.nineToSixteen.rawValue {
            eventTrackData.canvasRatio = "9:16"
        } else if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.oneToOne.rawValue {
            eventTrackData.canvasRatio = "1:1"
        } else if editProjectModel?.sData?.videoMetaData?.canvasType == videoCanvasType.sixteenToNine.rawValue {
            eventTrackData.canvasRatio = "16:9"
        }
        eventTrackData.syncedUpVideoNumber = selectedDataCount - selectedImageDataCount
        eventTrackData.syncedUpImageNumber = selectedImageDataCount
        eventTrackData.syncedUpOriginalMaterialDuration = selectedTotalDuration * 1000
        eventTrackData.syncedUpRhythmNumber = audioMixModel?.speed ?? 2
        eventTrackData.syncedUpVideoDuration = ((audioMixModel?.endTime ?? 0) - (audioMixModel?.startTime ?? 0)) * 1000
        // add by ak
        eventTrackData.syncedUpVideoType = rhythmMode
        eventTrackData.syncedUpVideoSpeedMax = syncedUpVideoSpeedMax
        eventTrackData.syncedUpVideoSpeedMin = syncedUpVideoSpeedMin

        return eventTrackData
    }

    /// 播放视频
    /// - Returns: description
    @objc func playVideo() {
        playBtn.isHidden = !playBtn.isHidden

        changPlayerIsPause(isPause: !playBtn.isHidden)
    }

    /// 按钮点击事件
    /// - Parameter sender: <#sender description#>
    /// - Returns: <#description#>
    @objc func btnClick(sender: UIButton) {
        switch sender.tag {
        case 1:
            if !(isExportSuccess && isSaveProjectSuccess && isUploadSuccess && isPublicSuccess) {
                cShowHUB(superView: nil, msg: "视频发布失败，请重新合成")
                return
            }
            if !PQSingletoWXApiUtil.shared.isInstallWX() {
                cShowHUB(superView: nil, msg: "您还未安装微信客户端！")
                return
            }
            cShowHUB(superView: nil, msg: nil)
            let shareId = getUniqueId(desc: "\(videoData?.uniqueId ?? "")shareId")
            PQBaseViewModel.wxFriendShareInfo(videoId: (videoData?.uniqueId)!) { [weak self] imagePath, title, shareWeappRawId, msg in
                if msg != nil {
                    cShowHUB(superView: nil, msg: "网络不佳哦")
                    return
                }
                self?.isShared = true
                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
                }
                cHiddenHUB(superView: nil)
            }
            // 点击上报：分享微信
            PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_shareWechat, pageSource: .sp_stuck_publishSyncedUp, extParams: ["videoId": videoData?.uniqueId ?? ""], remindmsg: "卡点视频数据上报-（点击上报：分享微信）")
        case 2:
            if !(isExportSuccess && isSaveProjectSuccess && isUploadSuccess && isPublicSuccess) {
                cShowHUB(superView: nil, msg: "视频发布失败，请重新合成")
                return
            }
            if !PQSingletoWXApiUtil.shared.isInstallWX() {
                cShowHUB(superView: nil, msg: "您还未安装微信客户端！")
                return
            }
            let shareId = getUniqueId(desc: "\(videoData?.uniqueId ?? "")shareId")
            PQBaseViewModel.h5ShareLinkInfo(videoId: videoData?.uniqueId ?? "", pageSource: videoData?.pageSource ?? .sp_category) { [weak self] path, _ in
                cHiddenHUB(superView: nil)
                if path != nil {
                    PQBaseViewModel.wxFriendShareInfo(videoId: (self?.videoData?.uniqueId)!) { [weak self] imagePath, _, _, msg in
                        if msg != nil {
                            cShowHUB(superView: nil, msg: "网络不佳哦")
                            return
                        }
                        self?.isShared = true

                        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
                        }
                    }
                } else {
                    cShowHUB(superView: nil, msg: "网络不佳哦")
                }
            }
            // 点击上报：分享朋友圈
            PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_shareWechatMoment, pageSource: .sp_stuck_publishSyncedUp, extParams: ["videoId": videoData?.uniqueId ?? ""], remindmsg: "卡点视频数据上报-（点击上报：分享朋友圈）")
        case 3:

            // 点击上报：完成
            PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_finished, pageSource: .sp_stuck_publishSyncedUp, extParams: ["videoId": videoData?.uniqueId ?? ""], remindmsg: "卡点视频数据上报-（点击上报：完成）")
            navigationController?.viewControllers = [(navigationController?.viewControllers.first)!]
            // 发送通知
            postNotification(name: cFinishedPublishedNotiKey)
        case 97:
            // 视频保存重试
            saveRetryBtn.isHidden = true
            saveVideoTipsLabel.text = "视频保存中..."
            saveStuckPointVideo()
        default:
            break
        }
    }

    /// 添加提示视图
    /// - Parameters:
    ///   - isNetCollected: <#isNetCollected description#>
    ///   - msg: <#msg description#>
    func showUploadRemindView(isNetCollected _: Bool = true, msg: String? = nil) {
        view.endEditing(true)
        
        let emptyData = PQEmptyModel()
        emptyData.isRefreshHidden = false
        emptyData.title = "上传失败"
        emptyData.titleColor = UIColor.hexColor(hexadecimal: "#353535")
        emptyData.summary = "建议切换 WIFI/移动网络后再重试"
        emptyData.summaryColor = UIColor.hexColor(hexadecimal: "#353535")
        emptyData.refreshBgColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
        emptyData.refreshTitle = NSMutableAttributedString(string: "立即重试", attributes: [.foregroundColor:UIColor.white])
        emptyData.emptySoureImage = UIImage.moduleImage(named: "stuckPoint_video_empty", moduleName: "BFMaterialKit",isAssets: false)
        emptyData.netDisRefreshBgColor = UIColor.hexColor(hexadecimal: "#FA6400")
        emptyData.netDisTitle = "内容加载失败"
        emptyData.netDisTitleColor = UIColor.hexColor(hexadecimal: "#333333")
        emptyData.netemptyDisImage = UIImage.moduleImage(named: "empty_netDis_icon", moduleName: "BFMaterialKit",isAssets: false)
        emptyData.netDisRefreshTitle = NSMutableAttributedString(string: "重新加载", attributes: [.font: UIFont.systemFont(ofSize: 16, weight: .medium), .foregroundColor: UIColor.white])
        
        let emptyRemindView = PQEmptyRemindView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: view.frame.width, height: view.frame.height - cDevice_iPhoneNavBarAndStatusBarHei))
//        emptyRemindView.isHidden = true
        emptyRemindView.emptyData = emptyData
        emptyRemindView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
        emptyRemindView.fullRefreshBloc = {[weak self] _, _ in
            if emptyRemindView.refreshBtn.currentAttributedTitle?.string == "立即重试" {
                emptyRemindView.isHidden = true
                // 重试逻辑
                if let message = msg{
                    if message.contains("token") {
                        self?.uploadVideo()
                    }else if message.contains("aliOss"){
                        self?.uploadVideo()
                    }
                }
            }
        }
        emptyRemindView.refreshBtn.addCorner(corner: 4)
        view.addSubview(emptyRemindView)
    
//        PQRemindView.showUploadRemindView(title: "上传失败", summary: (msg != nil ? msg! : "视频文件已丢失"), confirmTitle:  "立即重试") { [weak self] _, _ in
//            self?.navigationController?.popToViewController((self?.navigationController?.viewControllers[1])!, animated: true)
//        }
    }

    @objc func enterBackground() {
        BFLog(message: "进入到后台")
        // 取消导出
        if exporter != nil {
            exporter.cancel()
        }
        playBtn.isHidden = false
        avPlayer.pause()
    }

    @objc func willEnterForeground() {
        BFLog(message: "进入到前台")
        if !isExportSuccess {
            appendAudio()
        }

        playBtn.isHidden = true
        playerHeaderCoverImageView.isHidden = true
        avPlayer.play()
    }

    @objc func didBecomeActiveNotification() {
        if isShared {
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { [weak self] in
                self?.isShared = false
                cShowHUB(superView: nil, msg: "分享成功")
                self?.playBtn.isHidden = true
                self?.avPlayer.play()
            }
        }
    }

    /// 更新进度
    /// - Returns: <#description#>
    func updatePublicCurrentProgress(useProgress: Float) {
        exportProgrss = Int(useProgress * 100)
        progressView.setProgress(useProgress, animated: true)
        let attributedText = NSMutableAttributedString(string: "\(exportProgrss)%\n视频正在处理中，请勿离开")
        attributedText.addAttributes([.font: UIFont.systemFont(ofSize: 34)], range: NSRange(location: 0, length: "\(exportProgrss)%".count))
        progressTipsLab.attributedText = attributedText
    }

    func changPlayerIsPause(isPause: Bool) {
        if isPause {
            playBtn.isHidden = false
            avPlayer.pause()
            playerHeaderCoverImageView.isHidden = false

        } else {
            playBtn.isHidden = true
            avPlayer.play()
            playerHeaderCoverImageView.isHidden = true
        }
    }

    @objc func titleLabelClick() {
        BFLog(message: "点击输入框")
        changPlayerIsPause(isPause: true)

        pinView.isHidden = true
        publicTitleView.show()

        if publicTitleView.inputTV.text.count > 0 {
            publicTitleView.inputTV.text = titleLabel.text
        }

        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: "")

    }

    @objc func settingCoverImage() {
        if exportLocalURL == nil {
            BFLog(message: "导出的视频地址错误？？？。。。")
            return
        }
        changPlayerIsPause(isPause: true)

        let asset = AVURLAsset(url: exportLocalURL!, options: nil)

        publicEditCoverView.show(videoURL: exportLocalURL!, duration: CMTimeGetSeconds(asset.duration))

        // 点击了确认 btn
        publicEditCoverView.selectImageCallBack = { [weak self] imageData in

            self?.changPlayerIsPause(isPause: false)
            if imageData != nil {
                self?.coverImageView.image = imageData
                self?.playerHeaderCoverImageView.image = imageData
                self?.uploadData?.image = imageData
                self?.updateCoverImagegOrTitle()
            }
        }
        // 点击了从相册选择
        publicEditCoverView.selectPhotoBtnCallBack = { [weak self] in
            let imageSelected = PQImageSelectedController()
            imageSelected.isAssetImage = true

            imageSelected.videoWidth = CGFloat(self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)
            imageSelected.videoHeight = CGFloat(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)
//           imageSelected.uploadData = uploadData
            //        imageSelected.updataVideoData = updataVideoData
            self?.navigationController?.pushViewController(imageSelected, animated: true)
        }

        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: "")

    }

    // 更新标题或封面
    func updateCoverImagegOrTitle() {
        PQLoadingHUB.shared.showHUB(isMode: true)
        // SanW - 待修改 -
//        PQLoadingHUB.shared.showHUB(isMode: true)
        
        PQBaseViewModel.ossTempToken { [weak self] response, msg in
            let image: UIImage = (self?.uploadData?.image)!
            let data = image.jpegData(compressionQuality: 1)
            let accessKeyId: String = "\(response?["accessKeyId"] ?? "")"
            let secretKeyId: String = "\(response?["accessKeySecret"] ?? "")"
            let securityToken: String = "\(response?["securityToken"] ?? "")"
            let endpoint: String = "\(response?["endPoint"] ?? "")"
            let bucketName: String = "\(response?["bucketName"] ?? "")"
            let objectKey: String = "\(response?["objectKey"] ?? "")"
            _ = PQAliOssUtil.shared
                .startClient(
                    accessKeyId: accessKeyId,
                    secretKeyId: secretKeyId,
                    securityToken: securityToken,
                    endpoint: endpoint
                )
                .uploadObjectAsync(bucketName: bucketName, objectKey: objectKey, data: data!, fileExtensions: "png", imageUploadBlock: { _, code, ossObjectKey, _ in
                    if code == 1 && ossObjectKey == objectKey && ossObjectKey.count > 0 {

                        //add by ak 这里会在服务器生成分享使用的图片到1-2S 时间
                        PQUploadViewModel.updateVideo(title: self?.videoData?.title ?? "", videoId: self?.videoData?.uniqueId ?? "", coverImgPath: objectKey, descr: "") {_, newVideoData, msg in
                            
                            if newVideoData == nil {
                                cShowHUB(superView: self?.view, msg: msg)
                                // 可能有敏感词 要刷一组新标题并自动更新
                                let numberRandom: UInt32 = UInt32(arc4random_uniform(UInt32(self?.publicTitleView.titles.count ?? 0)))

                                self?.setTitleText(text: self?.publicTitleView.titles[Int(numberRandom)] ?? "")
                                self?.updateCoverImagegOrTitle()
                                sleep(UInt32(1.5))
                                PQLoadingHUB.shared.dismissHUB()

                                return
                            } else {
                                PQLoadingHUB.shared.dismissHUB()
                            }
                        }
                    } else {
                        PQLoadingHUB.shared.dismissHUB()
                    }
                })

    } }

    func setTitleText(text: String, textColor: UIColor = UIColor.hexColor(hexadecimal: "#ABABAB")) {
        selectTitle = text
        // 更新 UI
        titleLabel.text = text
        titleLabel.textColor = textColor
        publicTitleView.inputTV.placeHolder = text
        // 更新数据
        videoData?.title = text
    }

    // 取推荐的10个标题
    func getTitles() {
        PQBaseViewModel.getBaseConfig(completeHander: { [weak self] titles in

            if (titles?.count ?? 0) > 0 {
                var temp:Array<String> = titles!
                if((titles?.count ?? 0) <= 13){
                    for _ in 0 ... (13 - (titles?.count ?? 0)){
                        temp.append("")
                    }
                }
           
                self?.publicTitleView.titles = temp

                let numberRandom: UInt32 = UInt32(arc4random_uniform(UInt32(titles!.count)))
                BFLog(message: "接收到的 titles\(String(describing: titles))")
                self?.setTitleText(text: titles?[Int(numberRandom)] ?? "")
            }

        })
    }

    @objc func imageSelectedImage(notify: Notification) {
        let imageData: UIImage? = notify.userInfo?["image"] as? UIImage
        if imageData != nil {
            changPlayerIsPause(isPause: false)
            BFLog(message: "从系统相册选择了一个照片")
            publicEditCoverView.isHidden = true
            coverImageView.image = imageData
            playerHeaderCoverImageView.image = imageData
            uploadData?.image = imageData
            updateCoverImagegOrTitle()
        }
    }
}

// MARK: - 导出带水印+片尾的视频相关方法
extension PQStuckPointPublicController {
    //导出有水印的正片子
    func beginExportWatermarkMovie(inputAsset: AVURLAsset!) {
        if !(editProjectModel?.sData?.sections != nil && (editProjectModel?.sData?.sections.count ?? 0) > 0) {
            BFLog(message: "项目段落错误❌")
            return
        }
        // 输出视频地址
        var outPutMP4Path = exportVideosDirectory
        if !directoryIsExists(dicPath: outPutMP4Path) {
            BFLog(message: "文件夹不存在")
            createDirectory(path: outPutMP4Path)
        }
        outPutMP4Path.append("saveMovie_\(String.qe.timestamp()).mp4")
        let outPutMP4URL = URL(fileURLWithPath: outPutMP4Path)
        BFLog(message: "导出视频地址 \(outPutMP4URL)")

        watermarkMovieExporter = PQCompositionExporter(asset: inputAsset, videoComposition: nil, audioMix: nil, filters: nil, stickers: mStickers, animationTool: nil, exportURL: outPutMP4URL)
        watermarkMovieExporter.isAddWatermark = true
        var orgeBitRate = (editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * (editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 3

        if mStickers != nil {
            for stick in mStickers! {
                if stick.type == StickerType.VIDEO.rawValue {
                    let asset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + stick.locationPath), options: avAssertOptions)

                    let cbr = asset.tracks(withMediaType: .video).first?.estimatedDataRate
                    if Int(cbr ?? 0) > orgeBitRate {
                        orgeBitRate = Int(cbr ?? 0)
                    }
                }
            }
        }
        BFLog(message: "导出设置的码率为:\(orgeBitRate)")
        watermarkMovieExporter.showGaussianBlur = true
        if watermarkMovieExporter.prepare(videoSize: CGSize(width: editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0), videoAverageBitRate: orgeBitRate) {
            BFLog(message: "开始导出 \(String(describing: playeTimeRange.start)) 结束 \(String(describing: playeTimeRange.end))")
            watermarkMovieExporter.start(playeTimeRange: playeTimeRange)
            BFLog(message: "开始导出")
        }
        watermarkMovieExporter.progressClosure = { _, _, progress in
            BFLog(message: "带水印的合成进度 \(progress) ")
          
        }
        watermarkMovieExporter.completion = { [weak self] url in
            BFLog(message: "有水印的视频导出完成: \(String(describing: url)) 生成视频时长为：\(CMTimeGetSeconds(AVAsset(url: url ?? URL(string: "https://media.w3.org/2010/05/sintel/trailer.mp4")!).duration))")

            // 导出完成后取消导出
            if self?.watermarkMovieExporter != nil {
                self?.watermarkMovieExporter.cancel()
            }
 
            self?.watermarkMovieLocalURL = url
            
            //开始导出片尾 成功后自动保存到相册
            self?.beginExportEndMovie()
           
 
        }
    }
    //导出片尾视频
    func beginExportEndMovie() {
        if !(editProjectModel?.sData?.sections != nil && (editProjectModel?.sData?.sections.count ?? 0) > 0) {
            BFLog(message: "项目段落错误❌")
            return
        }
        // 输出视频地址
        var outPutMP4Path = exportVideosDirectory
        if !directoryIsExists(dicPath: outPutMP4Path) {
            BFLog(message: "文件夹不存在")
            createDirectory(path: outPutMP4Path)
        }
        outPutMP4Path.append("endMovie_\(String.qe.timestamp()).mp4")
        let outPutMP4URL = URL(fileURLWithPath: outPutMP4Path)
        BFLog(message: "导出视频地址 \(outPutMP4URL)")
        
        var orgeBitRate = (editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * (editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 3
        
        //片尾的视频素材地址
        let moveResPath = currentBundlePath()!.path(forResource:  (editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) <  (editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) ? "endMovieB" : "endMovieA", ofType: "mp4")
        if(moveResPath?.count ?? 0 == 0){
            BFLog(message: "片尾的视频素材地址无效！！！")
            return
        }
   
        let movieAsset = AVURLAsset(url: URL(fileURLWithPath: moveResPath!), options: avAssertOptions)
        let cbr = movieAsset.tracks(withMediaType: .video).first?.estimatedDataRate
        BFLog(message: "cbr  is\(cbr ?? 0)")
        if Int(cbr ?? 0) > orgeBitRate {
            orgeBitRate = Int(cbr ?? 0)
        }
        BFLog(message: "导出设置的码率为:\(orgeBitRate)")
  
        //头像保存沙盒地址
        BFLog(message: "头像的网络地址\(BFLoginUserInfo.shared.avatarUrl)")
        let avatarFilePath = NSHomeDirectory().appending("/Documents/").appending("user_avatar.jpg")
        
        // warning:给默认头像吧
        ImageDownloader.default.downloadImage(with: URL(string: BFLoginUserInfo.shared.avatarUrl)!, options: nil) {[weak self] result in
            var image : UIImage?
            switch result {
            case let .success(imageResult):  
                image = UIImage.nx_circleImage(imageResult.image)
                   
            case let .failure(error):
                image = UIImage.moduleImage(named: "user_avatar_normal", moduleName: "BFFramework", isAssets:false)
                BFLog(message: "下载头像图片失败：\(error.localizedDescription)")
            }
            if(image == nil){
                BFLog(message: "image date is error!!")
                return
            }
            UIImage.saveImage(currentImage: image!, outFilePath: avatarFilePath)
            
            //1,背景视频素材
            let bgMovieInfo:PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel.init()
            bgMovieInfo.type = StickerType.VIDEO.rawValue
            bgMovieInfo.locationPath = moveResPath ?? ""
            bgMovieInfo.timelineIn = 0
            bgMovieInfo.timelineOut = CMTimeGetSeconds(movieAsset.duration)
            bgMovieInfo.model_in = bgMovieInfo.timelineIn
            bgMovieInfo.out = bgMovieInfo.timelineOut
            bgMovieInfo.canvasFillType = stickerContentMode.aspectFitStr.rawValue
            //2,用户头像素材
            BFLog(message: "头像的沙盒地址:\(avatarFilePath)")
            let avatarSticker:PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel.init()
            avatarSticker.locationPath = avatarFilePath.replacingOccurrences(of: documensDirectory, with: "")
            avatarSticker.timelineIn = bgMovieInfo.timelineIn
            avatarSticker.timelineOut = bgMovieInfo.timelineOut
            avatarSticker.canvasFillType = stickerContentMode.aspectFitStr.rawValue

             //头像绘制大小\位置
            var avatarSize:Float = 0.0
            var avatarTop:Float = 0.0
            if((self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) > (self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)){
                 //竖屏
                avatarSize = Float(self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * 360.0 / 1080.0
                avatarTop = 430
            }else{
                //横屏屏
                avatarSize = Float(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 300.0 / 1080.0
                avatarTop = Float(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 130.0 / 1080.0
            }

            let avatarPostion:PQEditMaterialPositionModel = PQEditMaterialPositionModel.init()
            avatarPostion.width = Int(avatarSize)
            avatarPostion.height = Int(avatarSize)
            avatarPostion.x = ((self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) - Int(avatarSize)) / 2
            avatarPostion.y = Int(avatarTop)
            avatarSticker.materialPosition = avatarPostion
            
            //3,用户名素材
            let userNameSticker:PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel.init()
            userNameSticker.timelineIn = bgMovieInfo.timelineIn
            userNameSticker.timelineOut = bgMovieInfo.timelineOut
            userNameSticker.type = StickerType.SUBTITLE.rawValue
            
         
            //用户名绘制用到的参数
            var userNameTop:Float = 0.0
            var userNameFontSize:Float = 0.0
            if((self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) > (self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)){
             //竖屏
                userNameTop = 870
                userNameFontSize = Float(self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * 100.0 / 1080.0
            }else{
                 //横屏
                userNameTop = Float(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 480 / 1080.0
                userNameFontSize = Float(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 70.0 / 1080.0
            }
          
            let subtitleInfo:PQEditSubtitleInfoModel = PQEditSubtitleInfoModel.init()
            subtitleInfo.fontSize = Int(userNameFontSize)
            subtitleInfo.text = BFLoginUserInfo.shared.nickName
            userNameSticker.subtitleInfo = subtitleInfo

            let userNamePostion:PQEditMaterialPositionModel = PQEditMaterialPositionModel.init()
            userNamePostion.width = Int(userNameFontSize ) * 10
            userNamePostion.height = Int(userNameFontSize ) * 3
            userNamePostion.x = ((self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) -  userNamePostion.width) / 2
            userNamePostion.y = Int(userNameTop)
            userNameSticker.materialPosition = userNamePostion

            //4,音频
            let soundResPath = currentBundlePath()!.path(forResource: "endMovieSound", ofType: "mp3")
            let soundAsset = AVURLAsset(url:  URL(fileURLWithPath: soundResPath ?? ""), options: nil)
            self?.endMovieExporter = PQCompositionExporter(asset: soundAsset, videoComposition: nil, audioMix: nil, filters: nil, stickers: [bgMovieInfo,avatarSticker,userNameSticker], animationTool: nil, exportURL: outPutMP4URL)
            self?.endMovieExporter.isEndMovie = true
            if ((self?.endMovieExporter.prepare(videoSize: CGSize(width: self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0), videoAverageBitRate: orgeBitRate)) != nil) {

                self?.endMovieExporter.start(playeTimeRange: CMTimeRange.init(start: CMTime.zero, duration: CMTimeMakeWithSeconds(Float64(bgMovieInfo.out), preferredTimescale: BASE_FILTER_TIMESCALE)))
                BFLog(message: "开始导出")
            }
            self?.endMovieExporter.progressClosure = { _, _, progress in
                BFLog(message: "片尾合成进度 \(progress) ")
            }
            
            self?.endMovieExporter.completion = { [weak self] url in
                BFLog(message: "片尾的视频导出完成: \(String(describing: url)) 生成视频时长为：\(CMTimeGetSeconds(AVAsset(url: url ?? URL(string: "https://media.w3.org/2010/05/sintel/trailer.mp4")!).duration))")
                
                // 导出完成后取消导出
                if self?.endMovieExporter != nil {
                    self?.endMovieExporter.cancel()
                }
                self?.endMovieLocalURL = url
                //拼接水印正片和片尾
                if(self?.watermarkMovieLocalURL != nil && self?.endMovieLocalURL != nil){
                    let videoMerge:NXVideoMerge = NXVideoMerge.init()
                    videoMerge.mergeAndExportVideos(withFileURLs: [self!.watermarkMovieLocalURL!,self!.endMovieLocalURL!], renderSize:CGSize(width: self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)) { isSuccess, outFileURL in
                        if(isSuccess){
                            BFLog(message: "合并视频成功 outFilePath is \(outFileURL ?? "")")
                            self?.saveMovieLocalURL = outFileURL as? URL
                            //保存到相册 fp2-1-1 - 请求权限
                            self?.authorizationStatus()
                        }
                    }
                }
            }
        }

    }
    
    

}
