diff --git a/Veloria.xcodeproj/project.pbxproj b/Veloria.xcodeproj/project.pbxproj index b524f58..b60b857 100644 --- a/Veloria.xcodeproj/project.pbxproj +++ b/Veloria.xcodeproj/project.pbxproj @@ -146,6 +146,14 @@ BF5E75D32DE5692D00DE9DFE /* JXBaseAnimatedTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E75C12DE5692D00DE9DFE /* JXBaseAnimatedTransition.swift */; }; BF5E75D52DE56B2000DE9DFE /* VPHomeCagetoryRecommandContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E75D42DE56B2000DE9DFE /* VPHomeCagetoryRecommandContentCell.swift */; }; BF5E75DB2DE5B8B700DE9DFE /* VPMarqueeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E75DA2DE5B8B700DE9DFE /* VPMarqueeView.swift */; }; + BF692AB52E0644B500A5C2DA /* VPRevolutionSelectedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AB42E0644B500A5C2DA /* VPRevolutionSelectedView.swift */; }; + BF692AB72E06450C00A5C2DA /* VPRevolutionSelectedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AB62E06450C00A5C2DA /* VPRevolutionSelectedCell.swift */; }; + BFA21D982E01477200B3573D /* VPStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D972E01477200B3573D /* VPStoreViewController.swift */; }; + BFA21D9A2E01497F00B3573D /* VPStoreVipBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D992E01497F00B3573D /* VPStoreVipBuyView.swift */; }; + BFA21D9C2E01628F00B3573D /* VPStoreCoinsBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9B2E01628F00B3573D /* VPStoreCoinsBuyView.swift */; }; + BFA21D9E2E01684E00B3573D /* VPStoreCoinsItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9D2E01684E00B3573D /* VPStoreCoinsItemView.swift */; }; + BFA21DA02E01688D00B3573D /* VPStoreCoinsBigItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9F2E01688D00B3573D /* VPStoreCoinsBigItemView.swift */; }; + BFA21DA22E01696700B3573D /* VPStoreCoinsSmallItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21DA12E01696700B3573D /* VPStoreCoinsSmallItemView.swift */; }; BFCCE10D2DF951F600EDE165 /* SceneDelegate+APNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE10C2DF951ED00EDE165 /* SceneDelegate+APNS.swift */; }; BFCCE1122DF9638B00EDE165 /* VPVipAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE1112DF9638B00EDE165 /* VPVipAlertView.swift */; }; BFCCE1142DFAAC0900EDE165 /* VPLanguageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE1132DFAAC0900EDE165 /* VPLanguageViewController.swift */; }; @@ -162,12 +170,6 @@ BFCCE13D2DFFFAC500EDE165 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE13B2DFFFAC500EDE165 /* NotificationService.swift */; }; BFCCE1402E000ACE00EDE165 /* VPAnpsAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE13F2E000ACE00EDE165 /* VPAnpsAlertView.swift */; }; BFCCE1422E000F4800EDE165 /* VPHomePlayHistoricalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE1412E000F4800EDE165 /* VPHomePlayHistoricalView.swift */; }; - BFA21D982E01477200B3573D /* VPStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D972E01477200B3573D /* VPStoreViewController.swift */; }; - BFA21D9A2E01497F00B3573D /* VPStoreVipBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D992E01497F00B3573D /* VPStoreVipBuyView.swift */; }; - BFA21D9C2E01628F00B3573D /* VPStoreCoinsBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9B2E01628F00B3573D /* VPStoreCoinsBuyView.swift */; }; - BFA21D9E2E01684E00B3573D /* VPStoreCoinsItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9D2E01684E00B3573D /* VPStoreCoinsItemView.swift */; }; - BFA21DA02E01688D00B3573D /* VPStoreCoinsBigItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9F2E01688D00B3573D /* VPStoreCoinsBigItemView.swift */; }; - BFA21DA22E01696700B3573D /* VPStoreCoinsSmallItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21DA12E01696700B3573D /* VPStoreCoinsSmallItemView.swift */; }; BFF5AFA42DE6F15E0044227A /* VPMeVipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFA32DE6F15E0044227A /* VPMeVipCell.swift */; }; BFF5AFA62DE700420044227A /* VPMeVipPrivilegeItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFA52DE700420044227A /* VPMeVipPrivilegeItemView.swift */; }; BFF5AFA82DE704DC0044227A /* VPMeCoinCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFA72DE704DC0044227A /* VPMeCoinCell.swift */; }; @@ -418,6 +420,14 @@ BF5E75C92DE5692D00DE9DFE /* UIViewController+JXTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+JXTransition.swift"; sourceTree = ""; }; BF5E75D42DE56B2000DE9DFE /* VPHomeCagetoryRecommandContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeCagetoryRecommandContentCell.swift; sourceTree = ""; }; BF5E75DA2DE5B8B700DE9DFE /* VPMarqueeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPMarqueeView.swift; sourceTree = ""; }; + BF692AB42E0644B500A5C2DA /* VPRevolutionSelectedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPRevolutionSelectedView.swift; sourceTree = ""; }; + BF692AB62E06450C00A5C2DA /* VPRevolutionSelectedCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPRevolutionSelectedCell.swift; sourceTree = ""; }; + BFA21D972E01477200B3573D /* VPStoreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreViewController.swift; sourceTree = ""; }; + BFA21D992E01497F00B3573D /* VPStoreVipBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreVipBuyView.swift; sourceTree = ""; }; + BFA21D9B2E01628F00B3573D /* VPStoreCoinsBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsBuyView.swift; sourceTree = ""; }; + BFA21D9D2E01684E00B3573D /* VPStoreCoinsItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsItemView.swift; sourceTree = ""; }; + BFA21D9F2E01688D00B3573D /* VPStoreCoinsBigItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsBigItemView.swift; sourceTree = ""; }; + BFA21DA12E01696700B3573D /* VPStoreCoinsSmallItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsSmallItemView.swift; sourceTree = ""; }; BFCCE10C2DF951ED00EDE165 /* SceneDelegate+APNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SceneDelegate+APNS.swift"; sourceTree = ""; }; BFCCE1112DF9638B00EDE165 /* VPVipAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPVipAlertView.swift; sourceTree = ""; }; BFCCE1132DFAAC0900EDE165 /* VPLanguageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPLanguageViewController.swift; sourceTree = ""; }; @@ -433,12 +443,6 @@ BFCCE13B2DFFFAC500EDE165 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; BFCCE13F2E000ACE00EDE165 /* VPAnpsAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPAnpsAlertView.swift; sourceTree = ""; }; BFCCE1412E000F4800EDE165 /* VPHomePlayHistoricalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomePlayHistoricalView.swift; sourceTree = ""; }; - BFA21D972E01477200B3573D /* VPStoreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreViewController.swift; sourceTree = ""; }; - BFA21D992E01497F00B3573D /* VPStoreVipBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreVipBuyView.swift; sourceTree = ""; }; - BFA21D9B2E01628F00B3573D /* VPStoreCoinsBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsBuyView.swift; sourceTree = ""; }; - BFA21D9D2E01684E00B3573D /* VPStoreCoinsItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsItemView.swift; sourceTree = ""; }; - BFA21D9F2E01688D00B3573D /* VPStoreCoinsBigItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsBigItemView.swift; sourceTree = ""; }; - BFA21DA12E01696700B3573D /* VPStoreCoinsSmallItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsSmallItemView.swift; sourceTree = ""; }; BFF5AFA32DE6F15E0044227A /* VPMeVipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPMeVipCell.swift; sourceTree = ""; }; BFF5AFA52DE700420044227A /* VPMeVipPrivilegeItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPMeVipPrivilegeItemView.swift; sourceTree = ""; }; BFF5AFA72DE704DC0044227A /* VPMeCoinCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPMeCoinCell.swift; sourceTree = ""; }; @@ -1002,6 +1006,8 @@ BFF5B2742DF2C3750044227A /* VPDetailRecommandView.swift */, BFF5B2762DF2CA4B0044227A /* VPDetailRecommandBannerCell.swift */, BFF5B4AE2DF6B6630044227A /* VPMutualCollectionView.swift */, + BF692AB42E0644B500A5C2DA /* VPRevolutionSelectedView.swift */, + BF692AB62E06450C00A5C2DA /* VPRevolutionSelectedCell.swift */, ); path = View; sourceTree = ""; @@ -1480,10 +1486,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Veloria/Pods-Veloria-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Veloria/Pods-Veloria-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Veloria/Pods-Veloria-frameworks.sh\"\n"; @@ -1511,6 +1521,7 @@ BF0FA7B32DE447FE00C9E5F2 /* VPMeCell.swift in Sources */, BF0FA6DF2DDC5E4D00C9E5F2 /* String+VPAdd.swift in Sources */, BF0FA7002DDC665300C9E5F2 /* VPShortModel.swift in Sources */, + BF692AB72E06450C00A5C2DA /* VPRevolutionSelectedCell.swift in Sources */, 1B056E7B2DDB37BA007EE38D /* VPGradientView.swift in Sources */, BF0FA7262DDC8F7600C9E5F2 /* VPCollectionView.swift in Sources */, BF5E75DB2DE5B8B700DE9DFE /* VPMarqueeView.swift in Sources */, @@ -1554,6 +1565,7 @@ BF0FA73B2DDED1C700C9E5F2 /* VPHomeListCell.swift in Sources */, BF0FA79F2DE1A29A00C9E5F2 /* VPCollectListCell.swift in Sources */, BF0FA7592DDF1C2800C9E5F2 /* VPPlayerProgressView.swift in Sources */, + BF692AB52E0644B500A5C2DA /* VPRevolutionSelectedView.swift in Sources */, BFF5AFE02DEEC5AB0044227A /* VPPlayerVipBuyView.swift in Sources */, BFF5AFCA2DE97B7A0044227A /* VPWalletViewController.swift in Sources */, BFF5B26C2DF28FD50044227A /* VPStatAPI.swift in Sources */, diff --git a/Veloria/Base/Networking/API/VPVideoAPI.swift b/Veloria/Base/Networking/API/VPVideoAPI.swift index 6eeb411..3be2f4a 100644 --- a/Veloria/Base/Networking/API/VPVideoAPI.swift +++ b/Veloria/Base/Networking/API/VPVideoAPI.swift @@ -10,7 +10,7 @@ import UIKit class VPVideoAPI: NSObject { ///获取视频详情 - static func requestVideoDetail(shortPlayId: String, activityId: String? = nil, completer: ((_ model: VPVideoDetailModel?) -> Void)?) { + static func requestVideoDetail(shortPlayId: String, activityId: String? = nil, revolution: VPShortModel.VideoRevolution? = nil, completer: ((_ model: VPVideoDetailModel?) -> Void)?) { var parameters: [String : Any] = [ "short_play_id" : shortPlayId, "video_id" : "0" @@ -20,6 +20,10 @@ class VPVideoAPI: NSObject { parameters["activity_id"] = activityId } + if let revolution = revolution?.rawValue { + parameters["revolution"] = revolution + } + var param = VPNetworkParameters(path: "/getVideoDetails") param.method = .get param.parameters = parameters diff --git a/Veloria/Class/Login/View/VPLoginContentView.swift b/Veloria/Class/Login/View/VPLoginContentView.swift index 323edd2..915f8a7 100644 --- a/Veloria/Class/Login/View/VPLoginContentView.swift +++ b/Veloria/Class/Login/View/VPLoginContentView.swift @@ -9,6 +9,8 @@ import UIKit class VPLoginContentView: HWPanModalContentView { + var loginFinishBlock: (() -> Void)? + private lazy var bgView: UIImageView = { let imageView = UIImageView(image: UIImage(named: "bg_image_01")) return imageView @@ -167,6 +169,7 @@ extension VPLoginContentView { VPHUD.dismiss() guard let self = self else { return } if isFinish { + self.loginFinishBlock?() self.dismiss(animated: true) { } diff --git a/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift b/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift index 87c3c5f..28c07b4 100644 --- a/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift +++ b/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift @@ -162,6 +162,11 @@ extension VPDetailPlayerViewController { self.viewModel.handleUnlock = { [weak self] in self?.unlockVideo() } + + self.viewModel.updateDetailDataBlock = { [weak self] toIndexPath in + guard let self = self else { return } + self.requestDetailData(indexPath: self.viewModel.currentIndexPath) + } } } @@ -171,7 +176,7 @@ extension VPDetailPlayerViewController { let view = VPEpisodeView() view.dataArr = detailModel?.episodeList ?? [] view.shortModel = detailModel?.shortPlayInfo - view.currentIndex = self.currentIndexPath.row + view.currentIndex = self.viewModel.currentIndexPath.row view.didSelectedIndex = { [weak self] (index) in self?.scrollToItem(indexPath: IndexPath(row: index, section: 0), animated: false) } @@ -224,7 +229,7 @@ extension VPDetailPlayerViewController { view.videoId = videoInfo.short_play_video_id view.buyFinishBlock = { [weak self] in guard let self = self else { return } - self.requestDetailData(indexPath: self.currentIndexPath) + self.requestDetailData(indexPath: self.viewModel.currentIndexPath) } view.present(in: nil) } @@ -343,7 +348,7 @@ extension VPDetailPlayerViewController { VPHUD.show(containerView: self.view) - VPVideoAPI.requestVideoDetail(shortPlayId: shortPlayId, activityId: activityId) { [weak self] model in + VPVideoAPI.requestVideoDetail(shortPlayId: shortPlayId, activityId: activityId, revolution: self.viewModel.revolution) { [weak self] model in VPHUD.dismiss() guard let self = self else { return } guard let model = model else { return } diff --git a/Veloria/Class/Player/Controller/VPVideoPlayerViewController.swift b/Veloria/Class/Player/Controller/VPVideoPlayerViewController.swift index bdd34cd..14315ff 100644 --- a/Veloria/Class/Player/Controller/VPVideoPlayerViewController.swift +++ b/Veloria/Class/Player/Controller/VPVideoPlayerViewController.swift @@ -52,8 +52,6 @@ class VPVideoPlayerViewController: VPViewController { private(set) var dataArr: [Any] = [] - private(set) var currentIndexPath = IndexPath(row: 0, section: 0) - ///自动下一级 var autoNextEpisode = true @@ -120,7 +118,7 @@ class VPVideoPlayerViewController: VPViewController { self.viewModel.isPlaying = true - if getDataCount() - currentIndexPath.row <= 2 { + if getDataCount() - viewModel.currentIndexPath.row <= 2 { self.loadMoreData() } @@ -144,7 +142,7 @@ class VPVideoPlayerViewController: VPViewController { func clearDataArr() { self.dataArr.removeAll() self.viewModel.currentPlayer = nil - self.currentIndexPath = .init(row: 0, section: 0) + viewModel.currentIndexPath = .init(row: 0, section: 0) self.collectionView.contentOffset = .init(x: 0, y: 0) self.collectionView.reloadData() } @@ -176,7 +174,7 @@ class VPVideoPlayerViewController: VPViewController { func reloadData(completion: (() -> Void)? = nil) { CATransaction.setCompletionBlock { [weak self] in guard let self = self else { return } - let cell = self.collectionView.cellForItem(at: self.currentIndexPath) as? VPVideoPlayerCell + let cell = self.collectionView.cellForItem(at: viewModel.currentIndexPath) as? VPVideoPlayerCell self.viewModel.currentPlayer = cell completion?() @@ -190,7 +188,7 @@ class VPVideoPlayerViewController: VPViewController { CATransaction.setCompletionBlock { [weak self] in guard let self = self else { return } if !animated { - if self.currentIndexPath != indexPath, indexPath.row < self.getDataCount() { + if viewModel.currentIndexPath != indexPath, indexPath.row < self.getDataCount() { self.skip(indexPath: indexPath) } else { self.play() @@ -315,8 +313,8 @@ extension VPVideoPlayerViewController: UICollectionViewDelegate, UICollectionVie } } - if self.viewModel.currentPlayer == nil, indexPath == currentIndexPath, let playerProtocol = cell as? VPPlayerProtocol { - self.currentIndexPath = indexPath + if self.viewModel.currentPlayer == nil, indexPath == viewModel.currentIndexPath, let playerProtocol = cell as? VPPlayerProtocol { + viewModel.currentIndexPath = indexPath self.viewModel.currentPlayer = playerProtocol didChangeIndexPathForVisible() } @@ -349,7 +347,7 @@ extension VPVideoPlayerViewController: UICollectionViewDelegate, UICollectionVie for indexPath in indexPaths { guard let cell = self.collectionView.cellForItem(at: indexPath) else { continue } if floor(offsetY) == floor(cell.frame.origin.y) { - if self.currentIndexPath != indexPath { + if viewModel.currentIndexPath != indexPath { self.skip(indexPath: indexPath) } } @@ -357,7 +355,7 @@ extension VPVideoPlayerViewController: UICollectionViewDelegate, UICollectionVie } private func skip(indexPath: IndexPath) { - currentIndexPath = indexPath + viewModel.currentIndexPath = indexPath guard let currentPlayer = self.collectionView.cellForItem(at: indexPath) as? VPPlayerProtocol else { return } self.viewModel.currentPlayer = currentPlayer // currentCell = self.collectionView.cellForItem(at: indexPath) as? BCListPlayerCell @@ -381,7 +379,7 @@ extension VPVideoPlayerViewController { } private func didChangeIndexPathForVisible() { - self.delegate?.vp_playerListViewController?(self, didChangeIndexPathForVisible: self.currentIndexPath) + self.delegate?.vp_playerListViewController?(self, didChangeIndexPathForVisible: viewModel.currentIndexPath) } } diff --git a/Veloria/Class/Player/Model/VPShortModel.swift b/Veloria/Class/Player/Model/VPShortModel.swift index e435b95..4f489a4 100644 --- a/Veloria/Class/Player/Model/VPShortModel.swift +++ b/Veloria/Class/Player/Model/VPShortModel.swift @@ -10,6 +10,32 @@ import SmartCodable class VPShortModel: VPModel, SmartCodable { + enum VideoRevolution: String, SmartCaseDefaultable { + case r_540 = "540" + case r_720 = "720" + case r_1080 = "1080" + + var needLogin: Bool { + if self == .r_720 { + return true + } else { + return false + } + } + + var needVip: Bool { + if self == .r_1080 { + return true + } else { + return false + } + } + + var toString: String { + return "\(self.rawValue)P" + } + } + enum TagType: String, SmartCaseDefaultable { case hot = "hot" case new = "new" @@ -39,6 +65,8 @@ class VPShortModel: VPModel, SmartCodable { var video_url: String? var updated_at: String? + var revolution: VideoRevolution? + @IgnoredKey var titleAttributedString: NSAttributedString? @IgnoredKey diff --git a/Veloria/Class/Player/View/VPDetailPlayerControlView.swift b/Veloria/Class/Player/View/VPDetailPlayerControlView.swift index 2cb9971..23fee99 100644 --- a/Veloria/Class/Player/View/VPDetailPlayerControlView.swift +++ b/Veloria/Class/Player/View/VPDetailPlayerControlView.swift @@ -14,6 +14,7 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView { self.viewModel?.addObserver(self, forKeyPath: "rateModel", options: .new, context: nil) rateButton.setTitle(self.viewModel?.rateModel.formatString(), for: .normal) + revolutionButton.setNeedsUpdateConfiguration() } } @@ -30,6 +31,8 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView { epView.setTitle("veloria_EP.".localizedReplace(text: videoInfo?.episode ?? "0"), for: .normal) lockView.isHidden = !(videoInfo?.is_lock ?? false) lockView.videoInfo = videoInfo + + revolutionButton.setNeedsUpdateConfiguration() } } @@ -51,6 +54,15 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView { } } + override var isCurrent: Bool { + didSet { + if !isCurrent { + rateSelectedView.removeFromSuperview() + revolutionSelectedView.removeFromSuperview() + } + } + } + //MARK: -------------- UI属性 -------------- private lazy var bottomView: VPGradientView = { let view = VPGradientView() @@ -104,6 +116,23 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView { return button }() + ///分辨率 + private lazy var revolutionButton: UIButton = { + var config = UIButton.Configuration.plain() + config.contentInsets = .init(top: 0, leading: 12, bottom: 0, trailing: 12) + config.background.backgroundColor = .color949494(alpha: 0.4) + + let button = UIButton(configuration: config) + button.layer.cornerRadius = 15 + button.layer.masksToBounds = true + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + button.configuration?.attributedTitle = AttributedString.createAttributedString(string: self.viewModel?.revolution.toString ?? "", color: .colorFFFFFF(), font: .fontRegular(ofSize: 13)) + } + button.addTarget(self, action: #selector(handleRevolutionButton), for: .touchUpInside) + return button + }() + private lazy var timeLabel: UILabel = { let label = UILabel() label.font = .fontRegular(ofSize: 12) @@ -122,6 +151,16 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView { return view }() + ///分辨率选择 + private lazy var revolutionSelectedView: VPRevolutionSelectedView = { + let view = VPRevolutionSelectedView() + view.didSelected = { [weak self] revolution in + guard let self = self else { return } + self.viewModel?.selectedRevolution(revolution: revolution) + } + return view + }() + private lazy var lockView: VPVideoLockView = { let view = VPVideoLockView() view.clickUnlockButton = { [weak self] in @@ -176,6 +215,15 @@ extension VPDetailPlayerControlView { } } + @objc private func handleRevolutionButton() { + addSubview(revolutionSelectedView) + revolutionSelectedView.currentRevolution = self.viewModel?.revolution + + revolutionSelectedView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + } extension VPDetailPlayerControlView { @@ -188,6 +236,7 @@ extension VPDetailPlayerControlView { addSubview(epBgView) epBgView.addSubview(epView) epBgView.addSubview(allEpView) + addSubview(revolutionButton) addSubview(rateButton) addSubview(timeLabel) addSubview(lockView) @@ -224,9 +273,14 @@ extension VPDetailPlayerControlView { make.right.equalToSuperview().offset(-10) } + revolutionButton.snp.makeConstraints { make in + make.height.top.equalTo(epBgView) + make.left.equalTo(epBgView.snp.right).offset(10) + } + rateButton.snp.makeConstraints { make in make.right.equalToSuperview().offset(-15) - make.left.equalTo(epBgView.snp.right).offset(10) + make.left.equalTo(revolutionButton.snp.right).offset(10) make.height.top.equalTo(epBgView) make.width.equalTo(50) } diff --git a/Veloria/Class/Player/View/VPPlayerRechargeView.swift b/Veloria/Class/Player/View/VPPlayerRechargeView.swift index 61dbbd1..05b82be 100644 --- a/Veloria/Class/Player/View/VPPlayerRechargeView.swift +++ b/Veloria/Class/Player/View/VPPlayerRechargeView.swift @@ -23,6 +23,7 @@ class VPPlayerRechargeView: HWPanModalContentView { } var buyFinishBlock: (() -> Void)? + var vipBuyFinishBlock: (() -> Void)? //MARK: UI属性 private lazy var bgView: UIImageView = { @@ -64,7 +65,12 @@ class VPPlayerRechargeView: HWPanModalContentView { private lazy var vipView: VPStoreVipBuyView = { let view = VPStoreVipBuyView() view.buyFinishBlock = { [weak self] in - self?.buyFinishBlock?() + if let vipBuyFinishBlock = self?.vipBuyFinishBlock { + self?.vipBuyFinishBlock?() + } else { + self?.buyFinishBlock?() + } + self?.dismiss(animated: true) { } } diff --git a/Veloria/Class/Player/View/VPRevolutionSelectedCell.swift b/Veloria/Class/Player/View/VPRevolutionSelectedCell.swift new file mode 100644 index 0000000..0d3227a --- /dev/null +++ b/Veloria/Class/Player/View/VPRevolutionSelectedCell.swift @@ -0,0 +1,86 @@ +// +// VPRevolutionSelectedCell.swift +// Veloria +// +// Created by 湖南秦九 on 2025/6/21. +// + +import UIKit + +class VPRevolutionSelectedCell: VPCollectionViewCell { + + var videoRevolution: VPShortModel.VideoRevolution? { + didSet { + label.text = videoRevolution?.toString + + if videoRevolution?.needLogin == true { + markImageView.isHidden = false + markImageView.image = UIImage(named: "mark_icon_01") + } else if videoRevolution?.needVip == true { + markImageView.isHidden = false + markImageView.image = UIImage(named: "mark_icon_02") + } else { + markImageView.isHidden = true + } + + } + } + + var vp_isSelected: Bool = false { + didSet { + if vp_isSelected { + contentView.vp_setGradientBorder() + contentView.backgroundColor = .color1C2D2F(alpha: 0.6) + + } else { + contentView.vp_removeGradientBorder() + contentView.backgroundColor = .color000000(alpha: 0.3) + } + } + } + + private lazy var label: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 14) + label.textColor = .colorFFFFFF() + return label + }() + + private lazy var markImageView: UIImageView = { + let imageView = UIImageView() + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + vp_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + + +extension VPRevolutionSelectedCell { + + private func vp_setupUI() { + contentView.layer.cornerRadius = 6 + contentView.layer.masksToBounds = false + + + + contentView.addSubview(label) + contentView.addSubview(markImageView) + + label.snp.makeConstraints { make in + make.center.equalToSuperview() + } + + markImageView.snp.makeConstraints { make in + make.right.equalToSuperview() + make.top.equalToSuperview().offset(-5) + } + } + +} diff --git a/Veloria/Class/Player/View/VPRevolutionSelectedView.swift b/Veloria/Class/Player/View/VPRevolutionSelectedView.swift new file mode 100644 index 0000000..91c45c9 --- /dev/null +++ b/Veloria/Class/Player/View/VPRevolutionSelectedView.swift @@ -0,0 +1,107 @@ +// +// VPRevolutionSelectedView.swift +// Veloria +// +// Created by 湖南秦九 on 2025/6/21. +// + +import UIKit + +class VPRevolutionSelectedView: UIView { + + var currentRevolution: VPShortModel.VideoRevolution? { + didSet { + collectionView.reloadData() + } + } + + var didSelected: ((_ revolution: VPShortModel.VideoRevolution) -> Void)? + + private lazy var dataArr: [VPShortModel.VideoRevolution] = { + return [.r_540, .r_720, .r_1080] + }() + + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.itemSize = .init(width: 100, height: 54) + layout.minimumLineSpacing = 10 + layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15) + return layout + }() + + private lazy var collectionView: VPCollectionView = { + let collectionView = VPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.register(VPRevolutionSelectedCell.self, forCellWithReuseIdentifier: "cell") + collectionView.showsHorizontalScrollIndicator = false + collectionView.layer.masksToBounds = false + return collectionView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + let tap = UITapGestureRecognizer(target: self, action: #selector(handleDismiss)) + tap.delegate = self + self.addGestureRecognizer(tap) + vp_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc func handleDismiss() { + self.removeFromSuperview() + } + +} + + +extension VPRevolutionSelectedView { + + private func vp_setupUI() { + addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalToSuperview().offset(-(UIScreen.tabbarSafeBottomMargin + 85)) + make.height.equalTo(54) + } + } + +} + +//MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource -------------- +extension VPRevolutionSelectedView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let videoRevolution = dataArr[indexPath.row] + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! VPRevolutionSelectedCell + cell.videoRevolution = videoRevolution + cell.vp_isSelected = currentRevolution == videoRevolution + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataArr.count + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let videoRevolution = dataArr[indexPath.row] + self.didSelected?(videoRevolution) + self.handleDismiss() + } +} + +//MARK: -------------- UIGestureRecognizerDelegate -------------- +extension VPRevolutionSelectedView: UIGestureRecognizerDelegate { + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + if touch.view != self { + return false + } else { + return true + } + } +} diff --git a/Veloria/Class/Player/ViewModel/VPVideoPlayViewModel.swift b/Veloria/Class/Player/ViewModel/VPVideoPlayViewModel.swift index b25f972..283abf1 100644 --- a/Veloria/Class/Player/ViewModel/VPVideoPlayViewModel.swift +++ b/Veloria/Class/Player/ViewModel/VPVideoPlayViewModel.swift @@ -11,6 +11,7 @@ class VPVideoPlayViewModel: NSObject { @objc dynamic var isPlaying: Bool = true + var currentIndexPath = IndexPath(row: 0, section: 0) var currentPlayer: VPPlayerProtocol? { didSet { @@ -29,6 +30,9 @@ class VPVideoPlayViewModel: NSObject { } } + ///当前分辨率 + lazy var revolution: VPShortModel.VideoRevolution = .r_540 + ///设置进度 func seekToTime(toTime: Int) { self.currentPlayer?.seekToTime(toTime: toTime) @@ -44,4 +48,46 @@ class VPVideoPlayViewModel: NSObject { var handleEpisode: (() -> Void)? var handleUnlock: (() -> Void)? + + ///更新数据 + var updateDetailDataBlock: ((_ toIndexPath: IndexPath?) -> Void)? +} + +extension VPVideoPlayViewModel { + ///选择了新的分辨率 + func selectedRevolution(revolution: VPShortModel.VideoRevolution) { + guard self.revolution != revolution else { return } + let userInfo = VPLoginManager.manager.userInfo + + if revolution.needLogin, userInfo?.is_tourist != false { + + VPLoginManager.manager.openLogin { [weak self] in + guard let self = self else { return } + self.revolution = revolution + self.updateDetailDataBlock?(self.currentIndexPath) + } + + } else if revolution.needVip, userInfo?.is_vip != true { + guard let videoInfo = self.currentPlayer?.videoInfo else { return } + + let view = VPPlayerRechargeView() + view.shortPlayId = videoInfo.short_play_id + view.videoId = videoInfo.short_play_video_id + view.buyFinishBlock = { [weak self] in + guard let self = self else { return } + self.updateDetailDataBlock?(self.currentIndexPath) + } + view.vipBuyFinishBlock = { [weak self] in + guard let self = self else { return } + self.revolution = revolution + self.updateDetailDataBlock?(self.currentIndexPath) + } + view.present(in: nil) + } else { + self.revolution = revolution + self.updateDetailDataBlock?(self.currentIndexPath) + } + + } + } diff --git a/Veloria/Class/Wallet/Controller/VPStoreViewController.swift b/Veloria/Class/Wallet/Controller/VPStoreViewController.swift index 64f2b99..adac84b 100644 --- a/Veloria/Class/Wallet/Controller/VPStoreViewController.swift +++ b/Veloria/Class/Wallet/Controller/VPStoreViewController.swift @@ -8,6 +8,9 @@ import UIKit class VPStoreViewController: VPViewController { + + ///vip购买成功回调 + var vipBuyFinishBlock: (() -> Void)? private lazy var scrollView: VPScrollView = { let scrollView = VPScrollView() @@ -23,6 +26,10 @@ class VPStoreViewController: VPViewController { private lazy var vipView: VPStoreVipBuyView = { let view = VPStoreVipBuyView() + view.buyFinishBlock = { [weak self] in + guard let self = self else { return } + self.vipBuyFinishBlock?() + } return view }() diff --git a/Veloria/Libs/Login/VPLoginManager.swift b/Veloria/Libs/Login/VPLoginManager.swift index 5785521..f1ad00d 100644 --- a/Veloria/Libs/Login/VPLoginManager.swift +++ b/Veloria/Libs/Login/VPLoginManager.swift @@ -38,8 +38,9 @@ class VPLoginManager: NSObject { UserDefaults.vp_setObject(token, forKey: kVPLoginTokenDefaultsKey) } - func openLogin() { + func openLogin(finishHandle: (() -> Void)? = nil) { let view = VPLoginContentView() + view.loginFinishBlock = finishHandle view.present(in: nil) } diff --git a/Veloria/Source/Assets.xcassets/icon/mark_icon_01.imageset/Contents.json b/Veloria/Source/Assets.xcassets/icon/mark_icon_01.imageset/Contents.json new file mode 100644 index 0000000..8cfadf7 --- /dev/null +++ b/Veloria/Source/Assets.xcassets/icon/mark_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 79@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 79@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Veloria/Source/Assets.xcassets/icon/mark_icon_01.imageset/Group 79@2x.png b/Veloria/Source/Assets.xcassets/icon/mark_icon_01.imageset/Group 79@2x.png new file mode 100644 index 0000000..f04f1e0 Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/mark_icon_01.imageset/Group 79@2x.png differ diff --git a/Veloria/Source/Assets.xcassets/icon/mark_icon_01.imageset/Group 79@3x.png b/Veloria/Source/Assets.xcassets/icon/mark_icon_01.imageset/Group 79@3x.png new file mode 100644 index 0000000..5de74f5 Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/mark_icon_01.imageset/Group 79@3x.png differ diff --git a/Veloria/Source/Assets.xcassets/icon/mark_icon_02.imageset/Contents.json b/Veloria/Source/Assets.xcassets/icon/mark_icon_02.imageset/Contents.json new file mode 100644 index 0000000..bcde35f --- /dev/null +++ b/Veloria/Source/Assets.xcassets/icon/mark_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 78@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 78@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Veloria/Source/Assets.xcassets/icon/mark_icon_02.imageset/Group 78@2x.png b/Veloria/Source/Assets.xcassets/icon/mark_icon_02.imageset/Group 78@2x.png new file mode 100644 index 0000000..a429925 Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/mark_icon_02.imageset/Group 78@2x.png differ diff --git a/Veloria/Source/Assets.xcassets/icon/mark_icon_02.imageset/Group 78@3x.png b/Veloria/Source/Assets.xcassets/icon/mark_icon_02.imageset/Group 78@3x.png new file mode 100644 index 0000000..ce114c7 Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/mark_icon_02.imageset/Group 78@3x.png differ