diff --git a/Veloria/Base/Networking/API/VPVideoAPI.swift b/Veloria/Base/Networking/API/VPVideoAPI.swift index ad1823d..6eeb411 100644 --- a/Veloria/Base/Networking/API/VPVideoAPI.swift +++ b/Veloria/Base/Networking/API/VPVideoAPI.swift @@ -150,6 +150,39 @@ class VPVideoAPI: NSObject { completer?(response.data?.list) } } + + ///视频观看结束 + static func requestViewingFinish(shortPlayId: String, videoId: String, activityId: String) { + var param = VPNetworkParameters(path: "/activeAfterWatchingVideo") + param.isLoding = false + param.isToast = false + param.parameters = [ + "short_play_video_id" : videoId, + "short_play_id" : shortPlayId, + "activity_id" : activityId + ] + + VPNetwork.request(parameters: param) { (response: VPNetworkResponse) in + + } + } + + ///上报播放时长 + static func requestUploadPlayProgress(shortPlayId: String, videoId: String, seconds: Int) { + + var param = VPNetworkParameters(path: "/uploadHistorySeconds") + param.isLoding = false + param.isToast = false + param.parameters = [ + "video_id" : videoId, + "short_play_id" : shortPlayId, + "play_seconds" : seconds + ] + + VPNetwork.request(parameters: param) { (response: VPNetworkResponse) in + + } + } } extension VPVideoAPI { diff --git a/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift b/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift index af5cca3..63faf22 100644 --- a/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift +++ b/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift @@ -22,8 +22,14 @@ class VPDetailPlayerViewController: VPVideoPlayerViewController { private var detailModel: VPVideoDetailModel? + ///上一次上报播放时长的节点 + private var lastUploadProgress: Int = 0 + ///推荐列表 private lazy var recommandList: [VPShortModel] = [] + ///是否展示推荐 + private lazy var isShowRecommand = false + private var recommandTimer: Timer? //MARK: UI属性 private lazy var backButton: UIButton = { @@ -52,7 +58,8 @@ class VPDetailPlayerViewController: VPVideoPlayerViewController { self.delegate = self self.dataSource = self NotificationCenter.default.addObserver(self, selector: #selector(buyVipFinishNotification), name: VPIAPManager.buyVipFinishNotification, object: nil) - self.jx_popDelegate = self +// self.jx_popDelegate = self + self.jx_popGestureType = .disabled requestDetailData() requestRecommandDataArr() @@ -67,6 +74,9 @@ class VPDetailPlayerViewController: VPVideoPlayerViewController { self.navigationController?.setNavigationBarHidden(true, animated: true) } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + } override func play() { guard let videoInfo = self.viewModel.currentPlayer?.videoInfo else { return } @@ -84,27 +94,43 @@ class VPDetailPlayerViewController: VPVideoPlayerViewController { VPVideoAPI.requestCreatePlayHistory(videoId: videoInfo.short_play_video_id, shortPlayId: videoInfo.short_play_id) } + override func currentPlayFinish() { + if let videoInfo = self.viewModel.currentPlayer?.videoInfo, let shortPlayId = videoInfo.short_play_id, let videoId = videoInfo.short_play_video_id { + if let activityId = self.activityId { + //播放完成统计 + VPVideoAPI.requestViewingFinish(shortPlayId: shortPlayId, videoId: videoId, activityId: activityId) + } + + //播放完成上报进度为0 + VPVideoAPI.requestUploadPlayProgress(shortPlayId: shortPlayId, videoId: videoId, seconds: 0) + self.viewModel.currentPlayer?.videoInfo?.play_seconds = 0 + } + + super.currentPlayFinish() + } + + override func currentPlayTimeDidChange(time: Int) { + super.currentPlayTimeDidChange(time: time) + + //每播放5秒上报一次 播放时长小于上次上报时间也需要上报 + if (time >= lastUploadProgress + 5 || time < lastUploadProgress) && time >= 5 { + lastUploadProgress = time + uploadPlayProgress() + } + } + @objc func handleBackButton() { - guard recommandList.count > 0 else { + guard recommandList.count > 0, isShowRecommand else { self.handleBack() return } self.pause() - let view = VPDetailRecommandView() - view.dataArr = recommandList - view.clickCloseButton = { [weak self] in - self?.handleBack() - } - view.clickLookButton = { [weak self] model in - guard let self = self else { return } - self.shortPlayId = model.short_play_id - self.activityId = nil - self.requestDetailData() - } - view.present(in: nil) + onRecommandView() } + + } @@ -213,6 +239,43 @@ extension VPDetailPlayerViewController { } } + ///上报播放进度 + private func uploadPlayProgress() { + let videoInfo = self.viewModel.currentPlayer?.videoInfo + let currentTime = self.viewModel.currentPlayer?.currentPosition ?? 0 + let duration = self.viewModel.currentPlayer?.duration ?? 0 + + var time = currentTime + if currentTime >= duration { + time = 0 + } + self.viewModel.currentPlayer?.videoInfo?.play_seconds = time * 1000 + + guard let shortPlayId = videoInfo?.short_play_id, let videoId = videoInfo?.short_play_video_id else { return } + //上报播放时长 + VPVideoAPI.requestUploadPlayProgress(shortPlayId: shortPlayId, videoId: videoId, seconds: time * 1000) + + } + + private func onRecommandView() { + let view = VPDetailRecommandView() + view.dataArr = recommandList + view.clickCloseButton = { [weak self] in + self?.handleBack() + } + view.clickLookButton = { [weak self] model in + guard let self = self else { return } + self.shortPlayId = model.short_play_id + self.activityId = nil + self.requestDetailData() + } + view.present(in: nil) + } + + @objc private func handleRecommandTimer() { + self.isShowRecommand = true + } + } //MARK: -------------- VPPlayerListViewControllerDataSource -------------- @@ -248,19 +311,34 @@ extension VPDetailPlayerViewController: VPPlayerListViewControllerDelegate { } -//MARK: -------------- JXViewControllerPopDelegate -------------- -extension VPDetailPlayerViewController: JXViewControllerPopDelegate { - func viewControllerPopShouldScrollBegan() -> Bool { - self.handleBackButton() - return false +////MARK: -------------- JXViewControllerPopDelegate -------------- +//extension VPDetailPlayerViewController: JXViewControllerPopDelegate { +// func viewControllerPopShouldScrollBegan() -> Bool { +// self.handleBackButton() +// return false +// } +//} + +//MARK: -------------- APP生命周期 -------------- +extension VPDetailPlayerViewController { + + override func willResignActiveNotification() { + super.willResignActiveNotification() + uploadPlayProgress() } } - extension VPDetailPlayerViewController { private func requestDetailData() { guard let shortPlayId = shortPlayId else { return } + + isShowRecommand = false + recommandTimer?.invalidate() + recommandTimer = nil + recommandTimer = Timer.scheduledTimer(timeInterval: 6, target: YYWeakProxy(target: self), selector: #selector(handleRecommandTimer), userInfo: nil, repeats: false) + + VPHUD.show(containerView: self.view) VPVideoAPI.requestVideoDetail(shortPlayId: shortPlayId, activityId: activityId) { [weak self] model in VPHUD.dismiss() diff --git a/Veloria/Class/Player/View/VPDetailRecommandBannerCell.swift b/Veloria/Class/Player/View/VPDetailRecommandBannerCell.swift index a07e64a..9d88fe8 100644 --- a/Veloria/Class/Player/View/VPDetailRecommandBannerCell.swift +++ b/Veloria/Class/Player/View/VPDetailRecommandBannerCell.swift @@ -13,33 +13,77 @@ class VPDetailRecommandBannerCell: FSPagerViewCell { var model: VPShortModel? { didSet { coverImageView.vp_setImage(url: model?.image_url) + + player.setPlayUrl(url: model?.video_url ?? "") } } -// private lazy var player: VPPlayer = { -// -// }() + var isCurrentPlayer: Bool = false { + didSet { + if !isCurrentPlayer { + coverImageView.isHidden = false + } + } + } + + private(set) lazy var player: VPPlayer = { + let player = VPPlayer() + player.playerView = playerView + player.delegate = self + return player + }() + private lazy var coverImageView: VPImageView = { let imageView = VPImageView() - imageView.layer.cornerRadius = 14 - imageView.layer.masksToBounds = true return imageView }() + private lazy var playerView: UIView = { + let view = UIView() + return view + }() + override init(frame: CGRect) { super.init(frame: frame) + contentView.layer.cornerRadius = 14 + contentView.layer.masksToBounds = true + contentView.addSubview(playerView) contentView.addSubview(coverImageView) coverImageView.snp.makeConstraints { make in make.edges.equalToSuperview() } + + playerView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } } @MainActor required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } + +//MARK: -------------- SPPlayerDelegate -------------- +extension VPDetailRecommandBannerCell: VPPlayerDelegate { + func vp_player(_ player: VPPlayer, loadStateDidChange state: VPPlayer.LoadState) { + updateCoverShowState() + } + + func vp_player(_ player: VPPlayer, playStateDidChanged state: VPPlayer.PlayState) { + updateCoverShowState() + } + + ///更新封面显示状态 + private func updateCoverShowState() { + let loadState = self.player.loadState + let playState = self.player.playState + if isCurrentPlayer && (loadState == .playable || loadState == .playthroughOK) && (playState == .playing) { + self.coverImageView.isHidden = true + } + } +} diff --git a/Veloria/Class/Player/View/VPDetailRecommandView.swift b/Veloria/Class/Player/View/VPDetailRecommandView.swift index 0179f58..d06f376 100644 --- a/Veloria/Class/Player/View/VPDetailRecommandView.swift +++ b/Veloria/Class/Player/View/VPDetailRecommandView.swift @@ -28,6 +28,16 @@ class VPDetailRecommandView: HWPanModalContentView { var clickCloseButton: (() -> Void)? var clickLookButton: ((_ model: VPShortModel) -> Void)? + private var currentCell: VPDetailRecommandBannerCell? { + didSet { + if oldValue == currentCell { return } + + oldValue?.isCurrentPlayer = false + oldValue?.player.pause() + + currentCell?.isCurrentPlayer = true + } + } //MARK: UI属性 @@ -117,6 +127,10 @@ class VPDetailRecommandView: HWPanModalContentView { }() + deinit { + vpLog(message: "销毁") + } + override init(frame: CGRect) { super.init(frame: frame) @@ -175,8 +189,16 @@ extension VPDetailRecommandView { private func setVideoTitle() { let index = self.bannerView.currentIndex - let cell = self.bannerView.cellForItem(at: index) - vpLog(message: cell) + guard self.dataArr.count > index else { return } + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in + guard let self = self else { return } + + let cell = self.bannerView.cellForItem(at: index) as? VPDetailRecommandBannerCell + self.currentCell = cell + self.currentCell?.player.start() + } + let model = self.dataArr[index] videoTitleLabel.text = model.name @@ -279,6 +301,13 @@ extension VPDetailRecommandView: FSPagerViewDelegate, FSPagerViewDataSource { } func pagerViewDidEndDecelerating(_ pagerView: FSPagerView) { + let newCell = pagerView.cellForItem(at: pagerView.currentIndex) + if newCell == currentCell { + return + } + self.currentCell?.isCurrentPlayer = false + self.currentCell?.player.pause() + setVideoTitle() }