// // BRVideoDetailViewController.swift // BeeReel // // Created by 长沙鸿瑶 on 2025/6/30. // import UIKit class BRVideoDetailViewController: BRPlayerListViewController { override var contentSize: CGSize { return .init(width: UIScreen.width, height: UIScreen.height) } override var CellClass: BRPlayerListCell.Type { return BRDetailPlayerCell.self } var shortPlayId: String? var activityId: String? private var detailArr: [BRVideoDetailModel] = [] ///是否展示推荐 private lazy var isAllowShowRecommand = false private var recommandTimer: Timer? ///上一次上报播放时长的节点 private var lastUploadTime: TimeInterval = 0 //MARK: UI属性 private lazy var backButton: UIButton = { let button = UIButton(type: .custom) button.setImage(UIImage(named: "nav_back_icon_01"), for: .normal) button.addTarget(self, action: #selector(handleBackButton), for: .touchUpInside) return button }() ///弹窗视图 private weak var popUpView: UIView? override func viewDidLoad() { super.viewDidLoad() self.delegate = self self.dataSource = self self.fd_interactivePopDisabled = true self.requestDetailData() self.viewModel.asyncGetRecommandDataArr() br_setupUI() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.navigationController?.setNavigationBarHidden(true, animated: true) } override var previousVideoUrl: String? { let index = self.viewModel.currentIndexPath.row - 1 guard index > 0 else { return nil } let model = self.detailArr[self.viewModel.currentIndexPath.section] return model.episodeList?[index].video_url } override var nextVideoUrl: String? { let index = self.viewModel.currentIndexPath.row + 1 let model = self.detailArr[self.viewModel.currentIndexPath.section] guard index < (model.episodeList?.count ?? 0) else { return nil } return model.episodeList?[index].video_url } override func play() { let videoInfo = self.viewModel.currentPlayer?.videoInfo guard videoInfo?.is_lock == true else { super.play() BRVideoAPI.requestAddPlayHistory(videoId: self.viewModel.currentPlayer?.videoInfo?.short_play_video_id, shortPlayId: self.viewModel.currentPlayer?.videoInfo?.short_play_id) return } self.pause() let myCoins = BRLoginManager.manager.userInfo?.totalCoin ?? 0 let coins = videoInfo?.coins ?? 0 if myCoins < coins, self.viewModel.currentPlayer?.hasLastEpisodeUnlocked != true { self.viewModel.openRechargeView() } } @objc private func handleBackButton() { if !self.viewModel.recommandList.isEmpty, isAllowShowRecommand { self.pause() let view = BRVideoDetailRecommendView() view.videoList = self.viewModel.recommandList view.didDismissBlock = { [weak self] in self?.handleNavBack() } view.didSelectedVideo = { [weak self] model in guard let self = self else { return } self.shortPlayId = model.short_play_id self.requestDetailData() } view.present(in: nil) } else { self.handleNavBack() } } } extension BRVideoDetailViewController { private func br_setupUI() { view.addSubview(backButton) backButton.snp.makeConstraints { make in make.left.equalToSuperview().offset(15) make.top.equalToSuperview().offset(UIScreen.statusBarHeight + 10) } } } extension BRVideoDetailViewController { @objc private func handleRecommandTimer() { self.isAllowShowRecommand = true } } //MARK: -------------- BRPlayerViewModelDelegate -------------- //重写父类的代理 extension BRVideoDetailViewController { ///打开选集 override func br_onEpisodeView(viewModel: BRPlayerViewModel) { let indexPath = self.viewModel.currentIndexPath let model = self.detailArr[indexPath.section] let view = BREpisodeSelectorView() view.shortModel = model.shortPlayInfo view.epList = model.episodeList ?? [] view.index = viewModel.currentIndexPath.row view.didSelectedIndex = { [weak self] index in self?.scrollToItem(indexPath: IndexPath(row: index, section: indexPath.section), animated: false) } view.present(in: nil) self.popUpView = view } ///速率 override func br_clickRateButton(viewModel: BRPlayerViewModel) { let view = BRRateSelectorView() view.didSelectedRate = { [weak self] model in guard let self = self else { return } self.viewModel.rateModel = model } view.show() self.popUpView = view } override func br_playProgressDidChange(viewModel: BRPlayerViewModel, time: TimeInterval) { if (time >= lastUploadTime + 5 || time < lastUploadTime) && time >= 5 { lastUploadTime = time self.viewModel.uploadPlayTime() } } override func br_needUpdateAllData(viewModel: BRPlayerViewModel, scrollTo indexPath: IndexPath?) { self.requestDetailData(indexPath: indexPath) } } //MARK: -------------- BRPlayerListViewControllerDataSource BRPlayerListViewControllerDelegate -------------- extension BRVideoDetailViewController: BRPlayerListViewControllerDataSource, BRPlayerListViewControllerDelegate { func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let model = self.detailArr[indexPath.section] let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BRDetailPlayerCell cell.videoInfo = model.episodeList?[indexPath.row] cell.shortModel = model.shortPlayInfo let upRow = indexPath.row - 1 if upRow >= 0, let videoInfo = model.episodeList?[upRow], videoInfo.is_lock == true { cell.hasLastEpisodeUnlocked = true } else { cell.hasLastEpisodeUnlocked = false } return cell } func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.detailArr[section].episodeList?.count ?? 0 } func br_numberOfSections(in viewController: BRPlayerListViewController) -> Int { return self.detailArr.count } func br_shouldAutoScrollNextEpisode(_ viewController: BRPlayerListViewController) -> Bool { if self.popUpView == nil { return true } else { return false } } } extension BRVideoDetailViewController { private func requestDetailData(indexPath: IndexPath? = nil) { guard let shortPlayId = shortPlayId else { return } isAllowShowRecommand = false recommandTimer?.invalidate() recommandTimer = nil recommandTimer = Timer.scheduledTimer(timeInterval: 6, target: YYTextWeakProxy(target: self), selector: #selector(handleRecommandTimer), userInfo: nil, repeats: false) BRHUD.show(containerView: self.view) BRVideoAPI.requestVideoDetail(shortPlayId: shortPlayId, activityId: activityId) { [weak self] model in BRHUD.dismiss() guard let self = self else { return } guard let model = model else { return } self.detailArr.removeAll() self.detailArr.append(model) self.reloadData { [weak self] in guard let self = self else { return } self.play() var targetIndexPath = IndexPath(row: 0, section: 0) if let indexPath = indexPath, indexPath.row < (model.episodeList?.count ?? 0) { targetIndexPath = indexPath } else if let videoInfo = model.video_info { var row: Int? model.episodeList?.enumerated().forEach({ if $1.id == videoInfo.id { row = $0 } }) if let row = row { targetIndexPath = .init(row: row, section: 0) } } self.scrollToItem(indexPath: targetIndexPath, animated: false) } } } }