播放页面解锁功能开发
@ -172,6 +172,10 @@
|
||||
F39855482E33928400E2D28D /* BRStoreCoinBigCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39855472E33928400E2D28D /* BRStoreCoinBigCell.swift */; };
|
||||
F398554A2E33929C00E2D28D /* BRStoreCoinCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39855492E33929C00E2D28D /* BRStoreCoinCell.swift */; };
|
||||
F398554C2E3392C200E2D28D /* BRStoreCoinSmallCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F398554B2E3392C200E2D28D /* BRStoreCoinSmallCell.swift */; };
|
||||
F398554E2E34699F00E2D28D /* BRVideoLockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F398554D2E34699F00E2D28D /* BRVideoLockView.swift */; };
|
||||
F39855502E34782200E2D28D /* BRVideoUnlockModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F398554F2E34782200E2D28D /* BRVideoUnlockModel.swift */; };
|
||||
F39855522E347BDE00E2D28D /* BRVideoRechargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39855512E347BDE00E2D28D /* BRVideoRechargeView.swift */; };
|
||||
F39855542E34A49500E2D28D /* VPPayDataRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39855532E34A49500E2D28D /* VPPayDataRequest.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@ -355,6 +359,10 @@
|
||||
F39855472E33928400E2D28D /* BRStoreCoinBigCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRStoreCoinBigCell.swift; sourceTree = "<group>"; };
|
||||
F39855492E33929C00E2D28D /* BRStoreCoinCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRStoreCoinCell.swift; sourceTree = "<group>"; };
|
||||
F398554B2E3392C200E2D28D /* BRStoreCoinSmallCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRStoreCoinSmallCell.swift; sourceTree = "<group>"; };
|
||||
F398554D2E34699F00E2D28D /* BRVideoLockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRVideoLockView.swift; sourceTree = "<group>"; };
|
||||
F398554F2E34782200E2D28D /* BRVideoUnlockModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRVideoUnlockModel.swift; sourceTree = "<group>"; };
|
||||
F39855512E347BDE00E2D28D /* BRVideoRechargeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRVideoRechargeView.swift; sourceTree = "<group>"; };
|
||||
F39855532E34A49500E2D28D /* VPPayDataRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPPayDataRequest.swift; sourceTree = "<group>"; };
|
||||
F70FA1F4169364C4C53534CE /* Pods-BeeReel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BeeReel.release.xcconfig"; path = "Target Support Files/Pods-BeeReel/Pods-BeeReel.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -816,6 +824,8 @@
|
||||
BF02B7EA2E2E388800172177 /* BREpisodeMenuView.swift */,
|
||||
BF02B7F02E2E55E300172177 /* BRRateSelectorView.swift */,
|
||||
BF02B7F22E2E571600172177 /* BRRateSelectorCell.swift */,
|
||||
F398554D2E34699F00E2D28D /* BRVideoLockView.swift */,
|
||||
F39855512E347BDE00E2D28D /* BRVideoRechargeView.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
@ -827,6 +837,7 @@
|
||||
BFC676802E122733006659E5 /* BRPlayerProtocol.swift */,
|
||||
BFC676862E122E36006659E5 /* BRVideoDetailModel.swift */,
|
||||
BF02B7EE2E2E4BFD00172177 /* BRRateModel.swift */,
|
||||
F398554F2E34782200E2D28D /* BRVideoUnlockModel.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@ -1073,6 +1084,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F398553D2E336D3000E2D28D /* BRPayDateModel.swift */,
|
||||
F39855532E34A49500E2D28D /* VPPayDataRequest.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@ -1207,12 +1219,14 @@
|
||||
BFC676B12E137D2F006659E5 /* BRPopularPicksViewController.swift in Sources */,
|
||||
BF02B7FC2E2F262F00172177 /* BRGradientView.swift in Sources */,
|
||||
BFC676692E0E34DA006659E5 /* BRUserAPI.swift in Sources */,
|
||||
F398554E2E34699F00E2D28D /* BRVideoLockView.swift in Sources */,
|
||||
BFC676782E0E9553006659E5 /* BRSpotlightMainBaseCell.swift in Sources */,
|
||||
BFC676732E0E938B006659E5 /* BRTableView.swift in Sources */,
|
||||
BFC676932E126A62006659E5 /* BRSpotlightNewMainCell.swift in Sources */,
|
||||
BFC6768D2E123D6E006659E5 /* AttributedString+BRAdd.swift in Sources */,
|
||||
BF02B8392E30B30400172177 /* AlignedCollectionViewFlowLayout.swift in Sources */,
|
||||
BF3A56882E30E0DD009E5CF9 /* BREmpty.swift in Sources */,
|
||||
F39855542E34A49500E2D28D /* VPPayDataRequest.swift in Sources */,
|
||||
BF3338F52E1616B200B10F76 /* BRExploreControlView.swift in Sources */,
|
||||
F398552D2E33126D00E2D28D /* BRMineCoinItemView.swift in Sources */,
|
||||
BF692B132E0A7B9000A5C2DA /* BRUserInfo.swift in Sources */,
|
||||
@ -1250,6 +1264,7 @@
|
||||
BF692B782E0D3A1200A5C2DA /* BRHomeModuleItem.swift in Sources */,
|
||||
BF692B5A2E0AAADD00A5C2DA /* BRPlayerListCell.swift in Sources */,
|
||||
BF02B8312E30897700172177 /* BRSearchHomeView.swift in Sources */,
|
||||
F39855502E34782200E2D28D /* BRVideoUnlockModel.swift in Sources */,
|
||||
F398554A2E33929C00E2D28D /* BRStoreCoinCell.swift in Sources */,
|
||||
BF3A568C2E30EBA2009E5CF9 /* BRHomePlayRecordButton.swift in Sources */,
|
||||
BF692B162E0A7CD600A5C2DA /* BRHUD.swift in Sources */,
|
||||
@ -1287,6 +1302,7 @@
|
||||
F39855312E33620200E2D28D /* BRMineStoreCell.swift in Sources */,
|
||||
BF692B182E0A7D8900A5C2DA /* BRToast.swift in Sources */,
|
||||
BF692B0E2E0A7AF300A5C2DA /* UserDefaults+BRAdd.swift in Sources */,
|
||||
F39855522E347BDE00E2D28D /* BRVideoRechargeView.swift in Sources */,
|
||||
BF02B8082E2F616E00172177 /* BRFavoritesViewController.swift in Sources */,
|
||||
BF3338FD2E1626B000B10F76 /* BRPlayerControlProtocol.swift in Sources */,
|
||||
BF692B582E0AAA6F00A5C2DA /* UIScreen+BRAdd.swift in Sources */,
|
||||
|
@ -186,4 +186,8 @@ extension UIColor {
|
||||
static func colorFFB635(alpha: CGFloat = 1) -> UIColor {
|
||||
return UIColor(rgb: 0xFFB635, alpha: alpha)
|
||||
}
|
||||
|
||||
static func colorB5B5B5(alpha: CGFloat = 1) -> UIColor {
|
||||
return UIColor(rgb: 0xB5B5B5, alpha: alpha)
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +134,38 @@ class BRVideoAPI {
|
||||
}
|
||||
}
|
||||
|
||||
///上报播放时长
|
||||
static func requestUploadPlayTime(shortPlayId: String, videoId: String, seconds: Int) {
|
||||
|
||||
var param = BRNetworkParameters(path: "/uploadHistorySeconds")
|
||||
param.isLoding = false
|
||||
param.isToast = false
|
||||
param.parameters = [
|
||||
"video_id" : videoId,
|
||||
"short_play_id" : shortPlayId,
|
||||
"play_seconds" : seconds
|
||||
]
|
||||
|
||||
BRNetwork.request(parameters: param) { (response: BRNetworkResponse<String>) in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
///金币解锁视频
|
||||
static func requestCoinUnlockVideo(shortPlayId: String, videoId: String, completer: ((_ model: BRVideoUnlockModel?) -> Void)?) {
|
||||
|
||||
var param = BRNetworkParameters(path: "/buy_video")
|
||||
param.isLoding = true
|
||||
param.parameters = [
|
||||
"short_play_id" : shortPlayId,
|
||||
"video_id" : videoId,
|
||||
]
|
||||
|
||||
BRNetwork.request(parameters: param) { (response: BRNetworkResponse<BRVideoUnlockModel>) in
|
||||
completer?(response.data)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -72,9 +72,9 @@ class BRPanModalContentView: HWPanModalContentView {
|
||||
return false
|
||||
}
|
||||
|
||||
override func minVerticalVelocityToTriggerDismiss() -> CGFloat {
|
||||
return 0
|
||||
}
|
||||
// override func minVerticalVelocityToTriggerDismiss() -> CGFloat {
|
||||
// return 0
|
||||
// }
|
||||
|
||||
override func showsScrollableVerticalScrollIndicator() -> Bool {
|
||||
return false
|
||||
|
@ -49,6 +49,7 @@ class BRPlayerListViewController: BRViewController {
|
||||
private(set) lazy var viewModel: BRPlayerViewModel = {
|
||||
let vm = BRPlayerViewModel()
|
||||
vm.delegate = self
|
||||
vm.playerListVC = self
|
||||
return vm
|
||||
}()
|
||||
|
||||
@ -321,6 +322,14 @@ extension BRPlayerListViewController: BRPlayerViewModelDelegate {
|
||||
|
||||
}
|
||||
|
||||
func br_playProgressDidChange(viewModel: BRPlayerViewModel, time: TimeInterval) {
|
||||
|
||||
}
|
||||
|
||||
func br_needUpdateAllData(viewModel: BRPlayerViewModel, scrollTo indexPath: IndexPath?) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension BRPlayerListViewController {
|
||||
|
@ -23,6 +23,9 @@ class BRVideoDetailViewController: BRPlayerListViewController {
|
||||
|
||||
private var detailArr: [BRVideoDetailModel] = []
|
||||
|
||||
///上一次上报播放时长的节点
|
||||
private var lastUploadTime: TimeInterval = 0
|
||||
|
||||
//MARK: UI属性
|
||||
private lazy var backButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
@ -66,8 +69,21 @@ class BRVideoDetailViewController: BRPlayerListViewController {
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -116,6 +132,18 @@ extension BRVideoDetailViewController {
|
||||
|
||||
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 --------------
|
||||
@ -147,6 +175,8 @@ extension BRVideoDetailViewController: BRPlayerListViewControllerDataSource, BRP
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension BRVideoDetailViewController {
|
||||
@ -169,6 +199,23 @@ extension BRVideoDetailViewController {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ class BRRateModel: NSObject {
|
||||
|
||||
static func getAllRate() -> [BRRateModel] {
|
||||
return [
|
||||
BRRateModel(rate: .x0_25),
|
||||
// BRRateModel(rate: .x0_25),
|
||||
BRRateModel(rate: .x0_5),
|
||||
BRRateModel(rate: .x0_75),
|
||||
BRRateModel(rate: .x1),
|
||||
|
26
BeeReel/Class/Player/Model/BRVideoUnlockModel.swift
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// BRVideoUnlockModel.swift
|
||||
// BeeReel
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2025/7/26.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SmartCodable
|
||||
|
||||
class BRVideoUnlockModel: BRModel, SmartCodable {
|
||||
|
||||
enum ResponseStatus: String, SmartCaseDefaultable {
|
||||
///前面还有没购买的剧
|
||||
case jump = "jump"
|
||||
///没找到视频
|
||||
case noPlay = "no_play"
|
||||
///金币不足跳充值
|
||||
case notEnough = "not_enough"
|
||||
///购买成功
|
||||
case success = "success"
|
||||
}
|
||||
|
||||
var status: ResponseStatus?
|
||||
|
||||
}
|
@ -19,6 +19,12 @@ class BRDetailControlView: BRPlayerControlView {
|
||||
override var videoInfo: BRVideoInfoModel? {
|
||||
didSet {
|
||||
epButton.videoInfo = videoInfo
|
||||
videoLockView.videoInfo = videoInfo
|
||||
if videoInfo?.is_lock == true {
|
||||
self.videoLockView.isHidden = false
|
||||
} else {
|
||||
self.videoLockView.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,6 +114,14 @@ class BRDetailControlView: BRPlayerControlView {
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var videoLockView: BRVideoLockView = {
|
||||
let view = BRVideoLockView()
|
||||
view.clickUnlockButton = { [weak self] in
|
||||
self?.viewModel?.clickUnlockButton()
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
|
||||
@ -133,7 +147,6 @@ class BRDetailControlView: BRPlayerControlView {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension BRDetailControlView {
|
||||
@ -198,6 +211,7 @@ extension BRDetailControlView {
|
||||
addSubview(rateButton)
|
||||
addSubview(nameLabel)
|
||||
addSubview(favoriteButton)
|
||||
addSubview(videoLockView)
|
||||
|
||||
progressView.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview()
|
||||
@ -233,6 +247,10 @@ extension BRDetailControlView {
|
||||
make.right.equalToSuperview().offset(-7)
|
||||
make.bottom.equalTo(progressView.snp.top).offset(-10)
|
||||
}
|
||||
|
||||
videoLockView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ class BREpisodeSelectorCell: BRCollectionViewCell {
|
||||
var model: BRVideoInfoModel? {
|
||||
didSet {
|
||||
epLabel.text = model?.episode
|
||||
|
||||
lockView.isHidden = !(model?.is_lock ?? false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,6 +41,19 @@ class BREpisodeSelectorCell: BRCollectionViewCell {
|
||||
return imageView
|
||||
}()
|
||||
|
||||
private lazy var lockView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .colorE3FC37()
|
||||
view.br_setRoundedCorner(topLeft: 0, topRight: 6, bottomLeft: 6, bottomRight: 0)
|
||||
let icon = UIImageView(image: UIImage(named: "Frame 3"))
|
||||
view.addSubview(icon)
|
||||
icon.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
}
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
@ -51,6 +66,7 @@ class BREpisodeSelectorCell: BRCollectionViewCell {
|
||||
|
||||
contentView.addSubview(epLabel)
|
||||
contentView.addSubview(lightImageView)
|
||||
contentView.addSubview(lockView)
|
||||
|
||||
epLabel.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
@ -60,6 +76,12 @@ class BREpisodeSelectorCell: BRCollectionViewCell {
|
||||
make.right.top.equalToSuperview()
|
||||
}
|
||||
|
||||
lockView.snp.makeConstraints { make in
|
||||
make.top.right.equalToSuperview()
|
||||
make.width.equalTo(18)
|
||||
make.height.equalTo(12)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@MainActor required init?(coder: NSCoder) {
|
||||
|
@ -88,7 +88,17 @@ class BRPlayerControlView: UIView, BRPlayerControlProtocol {
|
||||
self.viewModel?.switchPlayAndPause()
|
||||
}
|
||||
|
||||
|
||||
func updatePlayIconState() {
|
||||
if videoInfo?.is_lock == true {
|
||||
self.playIconImageView.isHidden = true
|
||||
} else {
|
||||
if isCurrent == true, self.viewModel?.isPlaying != true {
|
||||
self.playIconImageView.isHidden = false
|
||||
} else {
|
||||
self.playIconImageView.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -107,13 +117,6 @@ extension BRPlayerControlView {
|
||||
|
||||
extension BRPlayerControlView {
|
||||
|
||||
private func updatePlayIconState() {
|
||||
if isCurrent == true, self.viewModel?.isPlaying != true {
|
||||
self.playIconImageView.isHidden = false
|
||||
} else {
|
||||
self.playIconImageView.isHidden = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -75,6 +75,9 @@ class BRPlayerListCell: BRCollectionViewCell, BRPlayerProtocol {
|
||||
self.player.seek(toTime: time)
|
||||
}
|
||||
|
||||
func seekTo(time: TimeInterval) {
|
||||
self.player.seek(toTime: time)
|
||||
}
|
||||
|
||||
var ControlViewClass: BRPlayerControlView.Type {
|
||||
return BRPlayerControlView.self
|
||||
@ -144,6 +147,7 @@ extension BRPlayerListCell: BRPlayerDelegate {
|
||||
} else {
|
||||
self.controlView.progress = time / player.duration
|
||||
}
|
||||
self.viewModel?.playProgressDidChange(time: time)
|
||||
}
|
||||
|
||||
func br_playerInBufferToPlay(_ player: BRPlayer) {
|
||||
@ -153,4 +157,11 @@ extension BRPlayerListCell: BRPlayerDelegate {
|
||||
func br_playerBufferingCompleted(_ player: BRPlayer) {
|
||||
self.controlView.isLoading = false
|
||||
}
|
||||
|
||||
func br_playerReadyToPlay(_ player: BRPlayer) {
|
||||
let time = TimeInterval(self.videoInfo?.play_seconds ?? 0) / 1000
|
||||
if time > 1 {
|
||||
self.seekTo(time: time)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
128
BeeReel/Class/Player/View/BRVideoLockView.swift
Normal file
@ -0,0 +1,128 @@
|
||||
//
|
||||
// BRVideoLockView.swift
|
||||
// BeeReel
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2025/7/26.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class BRVideoLockView: UIView {
|
||||
|
||||
|
||||
var videoInfo: BRVideoInfoModel? {
|
||||
didSet {
|
||||
lockButton.setNeedsUpdateConfiguration()
|
||||
}
|
||||
}
|
||||
|
||||
var clickUnlockButton: (() -> Void)?
|
||||
|
||||
private lazy var lockIconView: UIImageView = {
|
||||
let view = UIImageView(image: UIImage(named: "Frame 4"))
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var bottomView: UIView = {
|
||||
let view = UIImageView(image: UIImage(named: "bg"))
|
||||
view.isUserInteractionEnabled = true
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .fontBold(ofSize: 18)
|
||||
label.textColor = .colorFFFFFF()
|
||||
label.text = "Unlock to Continue".localized
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var lockButton: UIButton = {
|
||||
var config = UIButton.Configuration.plain()
|
||||
config.background.image = UIImage(named: "bg 1")
|
||||
config.image = UIImage(named: "Frame 5")
|
||||
config.imagePadding = 10
|
||||
|
||||
let button = UIButton(configuration: config)
|
||||
button.configurationUpdateHandler = { [weak self] button in
|
||||
guard let self = self else { return }
|
||||
|
||||
let title = "Unlocking costs ## Coins".localizedReplace(text: "\(videoInfo?.coins ?? 0)")
|
||||
|
||||
button.configuration?.attributedTitle = AttributedString.br_createAttributedString(string: title, color: .color1C1C1C(), font: .fontRegular(ofSize: 14))
|
||||
}
|
||||
button.addTarget(self, action: #selector(handleUnlockButton), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var totalCoinsLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .fontRegular(ofSize: 12)
|
||||
label.textColor = .colorB5B5B5()
|
||||
return label
|
||||
}()
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .color000000(alpha: 0.4)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(updateUserInfo), name: BRLoginManager.userInfoUpdateNotification, object: nil)
|
||||
|
||||
updateUserInfo()
|
||||
br_setupUI()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func updateUserInfo() {
|
||||
let userInfo = BRLoginManager.manager.userInfo
|
||||
totalCoinsLabel.text = "Balance: ## Coins".localizedReplace(text: "\(userInfo?.totalCoin ?? 0)")
|
||||
}
|
||||
|
||||
@objc private func handleUnlockButton() {
|
||||
self.clickUnlockButton?()
|
||||
}
|
||||
}
|
||||
|
||||
extension BRVideoLockView {
|
||||
|
||||
private func br_setupUI() {
|
||||
addSubview(lockIconView)
|
||||
addSubview(bottomView)
|
||||
bottomView.addSubview(titleLabel)
|
||||
bottomView.addSubview(lockButton)
|
||||
bottomView.addSubview(totalCoinsLabel)
|
||||
|
||||
lockIconView.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
}
|
||||
|
||||
bottomView.snp.makeConstraints { make in
|
||||
make.left.right.bottom.equalToSuperview()
|
||||
make.height.equalTo(UIScreen.tabbarSafeBottomMargin + 222)
|
||||
}
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalToSuperview().offset(26)
|
||||
}
|
||||
|
||||
lockButton.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.centerY.equalToSuperview()
|
||||
make.width.equalTo(260)
|
||||
make.height.equalTo(48)
|
||||
}
|
||||
|
||||
totalCoinsLabel.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.bottom.equalToSuperview().offset(-(UIScreen.tabbarSafeBottomMargin + 10))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
232
BeeReel/Class/Player/View/BRVideoRechargeView.swift
Normal file
@ -0,0 +1,232 @@
|
||||
//
|
||||
// BRVideoRechargeView.swift
|
||||
// BeeReel
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2025/7/26.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class BRVideoRechargeView: BRPanModalContentView {
|
||||
|
||||
var buyCoinsFinishBlock: (() -> Void)?
|
||||
var buyVipFinishBlock: (() -> Void)?
|
||||
|
||||
var payDataModel: BRPayDateModel? {
|
||||
didSet {
|
||||
self.stackView.br_removeAllArrangedSubview()
|
||||
self.vipView.list = payDataModel?.list_sub_vip ?? []
|
||||
self.coinView.list = payDataModel?.list_coins ?? []
|
||||
|
||||
if let sort = payDataModel?.sort, sort.count > 0 {
|
||||
sort.forEach {
|
||||
if $0 == .vip, payDataModel?.list_sub_vip?.isEmpty == false {
|
||||
self.stackView.addArrangedSubview(self.vipView)
|
||||
} else if $0 == .coin, payDataModel?.list_coins?.isEmpty == false {
|
||||
self.stackView.addArrangedSubview(self.coinView)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if payDataModel?.list_sub_vip?.isEmpty == false {
|
||||
self.stackView.addArrangedSubview(self.vipView)
|
||||
}
|
||||
if payDataModel?.list_coins?.isEmpty == false {
|
||||
self.stackView.addArrangedSubview(self.coinView)
|
||||
}
|
||||
}
|
||||
|
||||
self.setNeedsLayoutUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
var unlockCoin: Int? {
|
||||
didSet {
|
||||
if let unlockCoin = self.unlockCoin, unlockCoin > 0 {
|
||||
unlockCoinsView.isHidden = false
|
||||
} else {
|
||||
unlockCoinsView.isHidden = true
|
||||
}
|
||||
unlockCoinsView.setNeedsUpdateConfiguration()
|
||||
}
|
||||
}
|
||||
|
||||
var shortPlayId: String? {
|
||||
didSet {
|
||||
vipView.shortPlayId = shortPlayId
|
||||
coinView.shortPlayId = shortPlayId
|
||||
}
|
||||
}
|
||||
var videoId: String? {
|
||||
didSet {
|
||||
vipView.videoId = videoId
|
||||
coinView.videoId = videoId
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var coinIconView: UIView = {
|
||||
let imageView = UIImageView(image: UIImage(named: "Frame 6"))
|
||||
return imageView
|
||||
}()
|
||||
|
||||
private lazy var coinTitleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .fontMedium(ofSize: 13)
|
||||
label.textColor = .colorFFFFFF()
|
||||
label.text = "My Coins:".localized
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var coinLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .fontMedium(ofSize: 13)
|
||||
label.textColor = .colorE3FC37()
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var scrollView: BRScrollView = {
|
||||
let scrollView = BRScrollView()
|
||||
scrollView.bounces = false
|
||||
return scrollView
|
||||
}()
|
||||
|
||||
private lazy var unlockCoinsView: UIButton = {
|
||||
var config = UIButton.Configuration.plain()
|
||||
config.background.backgroundColor = .colorE3FC37()
|
||||
config.image = UIImage(named: "Frame 6")
|
||||
config.imagePadding = 2
|
||||
config.imagePlacement = .trailing
|
||||
config.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 10)
|
||||
|
||||
let button = UIButton(configuration: config)
|
||||
button.layer.cornerRadius = 13
|
||||
button.layer.masksToBounds = true
|
||||
button.isUserInteractionEnabled = false
|
||||
button.configurationUpdateHandler = { [weak self] button in
|
||||
guard let self = self else { return }
|
||||
let title = "Unlock:".localized
|
||||
let coins = " \(unlockCoin ?? 0)"
|
||||
|
||||
var attributedTitle = AttributedString.br_createAttributedString(string: title + coins, color: .color1C1C1C(), font: .fontRegular(ofSize: 13))
|
||||
if let range = attributedTitle.range(of: coins) {
|
||||
attributedTitle[range].font = UIFont.fontMedium(ofSize: 13)
|
||||
attributedTitle[range].foregroundColor = UIColor.colorFF7489()
|
||||
}
|
||||
button.configuration?.attributedTitle = attributedTitle
|
||||
}
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var stackView: UIStackView = {
|
||||
let stackView = UIStackView()
|
||||
stackView.axis = .vertical
|
||||
stackView.spacing = 12
|
||||
return stackView
|
||||
}()
|
||||
|
||||
private lazy var vipView: BRStoreVipView = {
|
||||
let view = BRStoreVipView()
|
||||
view.buyFinishBlock = { [weak self] in
|
||||
self?.buyVipFinishBlock?()
|
||||
self?.dismiss(animated: true) {
|
||||
|
||||
}
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var coinView: BRStoreCoinView = {
|
||||
let view = BRStoreCoinView()
|
||||
view.buyFinishBlock = { [weak self] in
|
||||
self?.buyCoinsFinishBlock?()
|
||||
self?.dismiss(animated: true) {
|
||||
|
||||
}
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(updateUserInfo), name: BRLoginManager.userInfoUpdateNotification, object: nil)
|
||||
self.contentHeight = UIScreen.height - UIScreen.navBarHeight
|
||||
self.mainScrollView = self.scrollView
|
||||
|
||||
br_setupUI()
|
||||
|
||||
updateUserInfo()
|
||||
}
|
||||
|
||||
@MainActor required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
override func allowsPullDownWhenShortState() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override func allowsDragToDismiss() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension BRVideoRechargeView {
|
||||
|
||||
private func br_setupUI() {
|
||||
addSubview(scrollView)
|
||||
scrollView.addSubview(stackView)
|
||||
addSubview(coinTitleLabel)
|
||||
addSubview(coinLabel)
|
||||
addSubview(coinIconView)
|
||||
addSubview(unlockCoinsView)
|
||||
|
||||
scrollView.snp.makeConstraints { make in
|
||||
make.left.right.bottom.equalToSuperview()
|
||||
make.top.equalToSuperview().offset(50)
|
||||
}
|
||||
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.top.left.equalToSuperview()
|
||||
make.width.equalTo(UIScreen.width)
|
||||
make.bottom.equalToSuperview().offset(-UIScreen.tabbarSafeBottomMargin - 10)
|
||||
}
|
||||
|
||||
coinTitleLabel.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(15)
|
||||
make.centerY.equalTo(coinIconView)
|
||||
}
|
||||
|
||||
coinLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(coinTitleLabel.snp.right)
|
||||
make.centerY.equalTo(coinIconView)
|
||||
}
|
||||
|
||||
coinIconView.snp.makeConstraints { make in
|
||||
make.left.equalTo(coinLabel.snp.right).offset(2)
|
||||
make.top.equalToSuperview().offset(18)
|
||||
}
|
||||
|
||||
unlockCoinsView.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(coinIconView)
|
||||
make.right.equalToSuperview().offset(-15)
|
||||
make.height.equalTo(26)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension BRVideoRechargeView {
|
||||
|
||||
@objc private func updateUserInfo() {
|
||||
let userInfo = BRLoginManager.manager.userInfo
|
||||
|
||||
coinLabel.text = " \(userInfo?.totalCoin ?? 0)"
|
||||
}
|
||||
|
||||
}
|
@ -10,6 +10,8 @@ import UIKit
|
||||
|
||||
@objc protocol BRPlayerViewModelDelegate {
|
||||
|
||||
@objc func br_playProgressDidChange(viewModel: BRPlayerViewModel, time: TimeInterval)
|
||||
|
||||
@objc func br_currentVideoPlayFinish(viewModel: BRPlayerViewModel)
|
||||
|
||||
@objc func br_switchPlayAndPause(viewModel: BRPlayerViewModel)
|
||||
@ -17,6 +19,9 @@ import UIKit
|
||||
@objc func br_onEpisodeView(viewModel: BRPlayerViewModel)
|
||||
|
||||
@objc func br_clickRateButton(viewModel: BRPlayerViewModel)
|
||||
|
||||
@objc func br_needUpdateAllData(viewModel: BRPlayerViewModel, scrollTo indexPath: IndexPath?)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +29,8 @@ class BRPlayerViewModel: NSObject {
|
||||
|
||||
weak var delegate: BRPlayerViewModelDelegate?
|
||||
|
||||
weak var playerListVC: BRPlayerListViewController?
|
||||
|
||||
@objc dynamic var isPlaying: Bool = true
|
||||
|
||||
var currentIndexPath = IndexPath(row: 0, section: 0)
|
||||
@ -44,12 +51,22 @@ class BRPlayerViewModel: NSObject {
|
||||
self.currentPlayer?.rate = rateModel.rate.getRate()
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var payDataRequest = VPPayDataRequest()
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension BRPlayerViewModel {
|
||||
|
||||
///播放进度变更
|
||||
func playProgressDidChange(time: TimeInterval) {
|
||||
if time > 1 {
|
||||
self.currentPlayer?.videoInfo?.play_seconds = Int(time) * 1000
|
||||
}
|
||||
self.delegate?.br_playProgressDidChange(viewModel: self, time: time)
|
||||
}
|
||||
|
||||
func playFinish(player: BRPlayerProtocol) {
|
||||
guard (player as? UICollectionViewCell) == (currentPlayer as? UICollectionViewCell) else { return }
|
||||
self.delegate?.br_currentVideoPlayFinish(viewModel: self)
|
||||
@ -73,7 +90,106 @@ extension BRPlayerViewModel {
|
||||
///设置进度
|
||||
func seekTo(progress: Float) {
|
||||
self.currentPlayer?.seekTo(progress: progress)
|
||||
}
|
||||
|
||||
///点击解锁按钮
|
||||
func clickUnlockButton() {
|
||||
unlockVideo { [weak self] finish in
|
||||
if finish {
|
||||
self?.playerListVC?.reloadData {
|
||||
self?.playerListVC?.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///更新全部数据
|
||||
func updateAllData(scrollTo indexPath: IndexPath? = nil) {
|
||||
|
||||
self.delegate?.br_needUpdateAllData(viewModel: self, scrollTo: indexPath)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension BRPlayerViewModel {
|
||||
func unlockVideo(completer: ((_ finish: Bool) -> Void)?) {
|
||||
let videoInfo = self.currentPlayer?.videoInfo
|
||||
guard let shortPlayId = videoInfo?.short_play_id, let videoId = videoInfo?.short_play_video_id else { return }
|
||||
|
||||
BRVideoAPI.requestCoinUnlockVideo(shortPlayId: shortPlayId, videoId: videoId) { [weak self] model in
|
||||
guard let self = self else { return }
|
||||
guard let model = model else {
|
||||
completer?(false)
|
||||
return
|
||||
}
|
||||
switch model.status {
|
||||
case .jump:
|
||||
BRToast.show(text: "beereel_jump_unlock_error".localized)
|
||||
completer?(false)
|
||||
|
||||
case .noPlay:
|
||||
BRToast.show(text: "beereel_buy_fail_toast_01".localized)
|
||||
completer?(false)
|
||||
|
||||
case .notEnough:
|
||||
self.openRechargeView()
|
||||
completer?(false)
|
||||
|
||||
case .success:
|
||||
//更新用户信息
|
||||
BRLoginManager.manager.updateUserInfo {
|
||||
videoInfo?.is_lock = false
|
||||
completer?(true)
|
||||
}
|
||||
|
||||
default:
|
||||
completer?(false)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///打开充值页面
|
||||
func openRechargeView() {
|
||||
guard let videoInfo = self.currentPlayer?.videoInfo else { return }
|
||||
|
||||
self.payDataRequest.requestProducts(isLoding: true) { [weak self] model in
|
||||
guard let self = self else { return }
|
||||
guard let model = model else { return }
|
||||
let view = BRVideoRechargeView()
|
||||
view.shortPlayId = videoInfo.short_play_id
|
||||
view.videoId = videoInfo.short_play_video_id
|
||||
view.payDataModel = model
|
||||
view.unlockCoin = self.currentPlayer?.videoInfo?.coins
|
||||
view.buyVipFinishBlock = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.updateAllData(scrollTo: self.currentIndexPath)
|
||||
}
|
||||
view.buyCoinsFinishBlock = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.updateAllData(scrollTo: self.currentIndexPath)
|
||||
}
|
||||
view.present(in: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///上报播放进度
|
||||
func uploadPlayTime() {
|
||||
let videoInfo = self.currentPlayer?.videoInfo
|
||||
let currentTime = self.currentPlayer?.currentTime ?? 0
|
||||
let duration = self.currentPlayer?.durationTime ?? 0
|
||||
|
||||
var time = currentTime
|
||||
if currentTime >= duration {
|
||||
time = 0
|
||||
}
|
||||
|
||||
guard let shortPlayId = videoInfo?.short_play_id, let videoId = videoInfo?.short_play_video_id else { return }
|
||||
|
||||
//上报播放时长
|
||||
BRVideoAPI.requestUploadPlayTime(shortPlayId: shortPlayId, videoId: videoId, seconds: Int(time) * 1000)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ class BRStoreViewController: BRViewController {
|
||||
|
||||
private var payData: BRPayDateModel?
|
||||
|
||||
private lazy var dataRequest = VPPayDataRequest()
|
||||
|
||||
private lazy var scrollView: BRScrollView = {
|
||||
let scrollView = BRScrollView()
|
||||
return scrollView
|
||||
@ -95,8 +97,7 @@ extension BRStoreViewController {
|
||||
extension BRStoreViewController {
|
||||
|
||||
private func requestPayData() {
|
||||
|
||||
BRStoreAPI.requestPayTemplate { [weak self] model in
|
||||
self.dataRequest.requestProducts { [weak self] model in
|
||||
guard let self = self else { return }
|
||||
guard let model = model else { return }
|
||||
|
||||
@ -123,7 +124,6 @@ extension BRStoreViewController {
|
||||
}
|
||||
|
||||
self.stackView.addArrangedSubview(self.tipView)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
114
BeeReel/Class/Store/Model/VPPayDataRequest.swift
Normal file
@ -0,0 +1,114 @@
|
||||
//
|
||||
// VPPayDataRequest.swift
|
||||
// BeeReel
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2025/7/26.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
|
||||
class VPPayDataRequest: NSObject {
|
||||
|
||||
private var oldTemplateModel: BRPayDateModel?
|
||||
|
||||
private var completerBlock: ((_ model: BRPayDateModel?) -> Void)?
|
||||
|
||||
private var isLoding = false
|
||||
private var isToast = false
|
||||
|
||||
|
||||
func requestProducts(isLoding: Bool = false, isToast: Bool = true, completer: ((_ model: BRPayDateModel?) -> Void)?) {
|
||||
self.completerBlock = completer
|
||||
self.isLoding = isLoding
|
||||
self.isToast = isToast
|
||||
|
||||
if isLoding {
|
||||
BRHUD.show()
|
||||
}
|
||||
|
||||
BRStoreAPI.requestPayTemplate { [weak self] model in
|
||||
guard let self = self else { return }
|
||||
if isLoding {
|
||||
BRHUD.dismiss()
|
||||
}
|
||||
completer?(model)
|
||||
|
||||
}
|
||||
|
||||
// BRStoreAPI.requestPayTemplate(isToast: isToast) { [weak self] model in
|
||||
// guard let self = self else { return }
|
||||
// guard let model = model else {
|
||||
// if isLoding {
|
||||
// VPHUD.dismiss()
|
||||
// }
|
||||
// self.completerBlock?(nil)
|
||||
// return
|
||||
// }
|
||||
// self.oldTemplateModel = model
|
||||
//
|
||||
// var productIdArr: [String] = []
|
||||
// model.list_sub_vip?.forEach { item in
|
||||
// productIdArr.append(VPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? "")
|
||||
// }
|
||||
// model.list_coins?.forEach { item in
|
||||
// productIdArr.append(VPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? "")
|
||||
// }
|
||||
//
|
||||
// let set = Set(productIdArr)
|
||||
// let productsRequest = SKProductsRequest(productIdentifiers: set)
|
||||
// productsRequest.delegate = self
|
||||
// productsRequest.start()
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
//MARK: -------------- SKProductsRequestDelegate --------------
|
||||
extension VPPayTemplateRequest: SKProductsRequestDelegate {
|
||||
|
||||
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
||||
if isLoding {
|
||||
VPHUD.dismiss()
|
||||
}
|
||||
|
||||
guard let templateModel = self.oldTemplateModel else { return }
|
||||
let products = response.products
|
||||
|
||||
var newCoinList: [VPPayTemplateItem] = []
|
||||
var newVipList: [VPPayTemplateItem] = []
|
||||
|
||||
templateModel.list_coins?.forEach { item in
|
||||
let productId = VPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? ""
|
||||
for product in products {
|
||||
if productId == product.productIdentifier {
|
||||
item.price = product.price.stringValue
|
||||
item.currency = product.priceLocale.currencySymbol
|
||||
newCoinList.append(item)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
templateModel.list_sub_vip?.forEach { item in
|
||||
let productId = VPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? ""
|
||||
for product in products {
|
||||
if productId == product.productIdentifier {
|
||||
item.price = product.price.stringValue
|
||||
item.currency = product.priceLocale.currencySymbol
|
||||
newVipList.append(item)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
templateModel.list_coins = newCoinList
|
||||
templateModel.list_sub_vip = newVipList
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.completerBlock?(templateModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
@ -9,6 +9,8 @@ import UIKit
|
||||
|
||||
class BRStoreCoinView: UIView {
|
||||
|
||||
var buyFinishBlock: (() -> Void)?
|
||||
|
||||
var list: [BRPayItem] = [] {
|
||||
didSet {
|
||||
|
||||
@ -30,6 +32,9 @@ class BRStoreCoinView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
var shortPlayId: String?
|
||||
var videoId: String?
|
||||
|
||||
private var newList: [[BRPayItem]] = []
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
|
@ -9,12 +9,17 @@ import UIKit
|
||||
|
||||
class BRStoreVipView: UIView {
|
||||
|
||||
var buyFinishBlock: (() -> Void)?
|
||||
|
||||
var list: [BRPayItem] = [] {
|
||||
didSet {
|
||||
self.collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
var shortPlayId: String?
|
||||
var videoId: String?
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = .colorFFFFFF()
|
||||
|
@ -10,6 +10,8 @@ import SJBaseVideoPlayer
|
||||
|
||||
|
||||
@objc protocol BRPlayerDelegate: NSObjectProtocol {
|
||||
|
||||
@objc optional func br_playerReadyToPlay(_ player: BRPlayer)
|
||||
///更新当前总进度
|
||||
@objc optional func br_playerDurationDidChange(_ player: BRPlayer, duration: TimeInterval)
|
||||
///更新当前进度
|
||||
@ -150,8 +152,6 @@ extension BRPlayer {
|
||||
//播放控制改变的回调
|
||||
self.player.playbackObserver.timeControlStatusDidChangeExeBlock = { [weak self] player in
|
||||
guard let self = self else { return }
|
||||
// , player.reasonForWaitingToPlay == SJWaitingToMinimizeStallsReason
|
||||
|
||||
if player.timeControlStatus == .waitingToPlay {//缓冲中
|
||||
self.delegate?.br_playerInBufferToPlay?(self)
|
||||
brLog(message: "=======缓冲中 === \(player.reasonForWaitingToPlay ?? "")")
|
||||
@ -161,6 +161,14 @@ extension BRPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
self.player.playbackObserver.assetStatusDidChangeExeBlock = { [weak self] player in
|
||||
guard let self = self else { return }
|
||||
brLog(message: "assetStatus === \(player.assetStatus.rawValue)")
|
||||
if player.assetStatus == .readyToPlay {
|
||||
self.delegate?.br_playerReadyToPlay?(self)
|
||||
}
|
||||
}
|
||||
|
||||
//播放时长改变
|
||||
self.player.playbackObserver.durationDidChangeExeBlock = { [weak self] player in
|
||||
guard let self = self else { return }
|
||||
|
22
BeeReel/Sources/Assets.xcassets/icon/Frame 3.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/Frame 3.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 375 B |
BIN
BeeReel/Sources/Assets.xcassets/icon/Frame 3.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 487 B |
22
BeeReel/Sources/Assets.xcassets/icon/Frame 4.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/Frame 4.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/Frame 4.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 2.3 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/Frame 5.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/Frame 5.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 757 B |
BIN
BeeReel/Sources/Assets.xcassets/icon/Frame 5.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.0 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/Frame 6.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/Frame 6.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/Frame 6.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 2.3 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/bg 1.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "bg@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "bg@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/bg 1.imageset/bg@2x.png
vendored
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/bg 1.imageset/bg@3x.png
vendored
Normal file
After Width: | Height: | Size: 36 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/bg.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "bg@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "bg@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/bg.imageset/bg@2x.png
vendored
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/bg.imageset/bg@3x.png
vendored
Normal file
After Width: | Height: | Size: 93 KiB |
@ -34,6 +34,41 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Balance: ## Coins" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Balance: ## Coins"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"beereel_buy_fail_toast_01" : {
|
||||
"comment" : "解锁失败提示",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Purchase failed, please try again later!"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"beereel_jump_unlock_error" : {
|
||||
"comment" : "解锁上一集提示",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "The prequel to this series is not unlocked. Please unlock the prequel before unlocking this series"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"beereel_network" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@ -320,6 +355,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"My Coins:" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "My Coins:"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"New Releases" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@ -474,6 +520,39 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Unlock to Continue" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Unlock to Continue"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Unlock:" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Unlock:"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Unlocking costs ## Coins" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Unlocking costs ## Coins"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"User Agreement" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
|