Commit bc28e536 authored by pete谢兆麟's avatar pete谢兆麟

Merge branch 'yinhe-live-1212' of...

Merge branch 'yinhe-live-1212' of http://gitlab.galaxy-immi.com/mobile-group/galaxy-iOS into yinhe-live-1212
parents a074d0a3 800b0929
......@@ -7172,7 +7172,7 @@
CODE_SIGN_ENTITLEMENTS = galaxy/galaxyTestEnv.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = RXHYW88XR7;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
FRAMEWORK_SEARCH_PATHS = (
......@@ -7314,7 +7314,7 @@
CODE_SIGN_ENTITLEMENTS = galaxy/galaxy.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = RXHYW88XR7;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
FRAMEWORK_SEARCH_PATHS = (
......@@ -7519,7 +7519,7 @@
CODE_SIGN_ENTITLEMENTS = galaxy/galaxyDebug.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = RXHYW88XR7;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
FRAMEWORK_SEARCH_PATHS = (
......@@ -7566,7 +7566,7 @@
CODE_SIGN_ENTITLEMENTS = galaxy/galaxy.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = RXHYW88XR7;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
FRAMEWORK_SEARCH_PATHS = (
......
......@@ -10,6 +10,52 @@ import ESTabBarController_swift
class YHAITabBarItemContentView: ESTabBarItemContentView {
var timer: Timer?
lazy var tipsView: UIView = {
let v = UIView()
let grayView = UIView()
grayView.backgroundColor = UIColor(hex: 0x000000, alpha: 0.6)
grayView.layer.cornerRadius = 6.0
grayView.clipsToBounds = true
v.addSubview(grayView)
let label = UILabel()
label.textColor = UIColor.white
label.textAlignment = .center
label.font = UIFont.PFSC_R(ofSize:12)
label.text = "Hi 我是港小宝,有什么需要帮助的,来找我吧~"
grayView.addSubview(label)
let arrowImgV = UIImageView(image: UIImage(named: "ai_tab_down_arrow"))
v.addSubview(arrowImgV)
arrowImgV.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.width.equalTo(24)
make.height.equalTo(6)
make.bottom.equalTo(-4)
}
label.snp.makeConstraints { make in
make.left.equalTo(15)
make.right.equalTo(-15)
make.height.equalTo(34)
make.top.bottom.equalToSuperview()
}
grayView.snp.makeConstraints { make in
make.left.equalTo(0)
make.right.equalTo(0)
make.top.equalToSuperview()
make.bottom.equalTo(arrowImgV.snp.top)
}
return v
}()
lazy var bottomTitleLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.mainTextColor
......@@ -26,29 +72,66 @@ class YHAITabBarItemContentView: ESTabBarItemContentView {
override init(frame: CGRect) {
super.init(frame: frame)
createUI()
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func updateLayout() {
super.updateLayout()
}
func createUI() {
self.removeSubviews()
self.addSubview(iconImgView)
self.addSubview(bottomTitleLabel)
self.addSubview(tipsView)
bottomTitleLabel.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.height.equalTo(14)
}
iconImgView.snp.makeConstraints { make in
make.width.height.equalTo(48.0)
make.bottom.equalTo(bottomTitleLabel.snp.top)
make.centerX.equalToSuperview()
}
tipsView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalTo(iconImgView.snp.top)
}
startTimer()
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
func startTimer() {
// 每隔 1 秒调用 timerFired 方法
timer = Timer.scheduledTimer(timeInterval: 5.0,
target: self,
selector: #selector(timerFired),
userInfo: nil,
repeats: false)
}
override func updateLayout() {
super.updateLayout()
@objc func timerFired() {
stopTimer()
tipsView.isHidden = true
}
// 停止定时器
func stopTimer() {
timer?.invalidate()
timer = nil
}
deinit {
// 确保在视图控制器被销毁时停止定时器
stopTimer()
}
}
......@@ -159,16 +159,7 @@ class YHAIMainChatViewController: YHBaseViewController {
var results = self.viewModel.historyMessages.map {
return $0.convertToChatMessage()
}
if !isPull {
if results.count <= 0 { // 无聊天记录时隐藏下拉组件
tableView.header = nil
} else {
tableView.es.addYHPullToRefresh {
self.getHistoryMessages(true)
}
}
}
let uuids = messages.map {
return $0.messageId
}
......
......@@ -272,43 +272,43 @@ extension YHAIRequestManager {
UserDefaults.standard.synchronize()
}
func getSubRobotChatConversationId(robotId: String, completion:((String)->())?) {
func getSubRobotChatConversationId(listItemId: String, completion:((String)->())?) {
let localConversationId = getLobalSubRobotConversationId(robotId: robotId)
let localConversationId = getLobalSubRobotConversationId(listItemId: listItemId)
if !localConversationId.isEmpty {
completion?(localConversationId)
return
}
self.viewModel.requestConversationId { sessionId in
self.saveLobalSubRobotConversationId(sessionId, robotId: robotId)
self.saveLobalSubRobotConversationId(sessionId, listItemId: listItemId)
completion?(sessionId)
}
}
func getLobalSubRobotConversationId(robotId: String) -> String {
func getLobalSubRobotConversationId(listItemId: String) -> String {
let userId = YHLoginManager.shared.userModel?.id ?? ""
let key = Self.subrobotConversationConfigKey + userId
if let dict = UserDefaults.standard.dictionary(forKey: key) {
if let conversationId = dict["\(robotId)"] as? String, !conversationId.isEmpty {
if let conversationId = dict["\(listItemId)"] as? String, !conversationId.isEmpty {
return conversationId
}
}
return ""
}
func saveLobalSubRobotConversationId(_ converId: String, robotId: String) {
func saveLobalSubRobotConversationId(_ converId: String, listItemId: String) {
let userId = YHLoginManager.shared.userModel?.id ?? ""
let key = Self.subrobotConversationConfigKey + userId
var dict:[String : Any] = [:]
if let config = UserDefaults.standard.dictionary(forKey: key) {
dict = config
}
dict["\(robotId)"] = converId
dict["\(listItemId)"] = converId
UserDefaults.standard.set(dict, forKey: key)
UserDefaults.standard.synchronize()
}
func clearLobalSubRobotConversationIdForRobotId(_ robotId: String) {
saveLobalSubRobotConversationId("", robotId: robotId)
func clearLobalSubRobotConversationIdForListItemId(_ listItemId: String) {
saveLobalSubRobotConversationId("", listItemId: listItemId)
}
}
......@@ -14,11 +14,12 @@ class YHAIRobotChatViewController: YHBaseViewController {
var myTitle: String = ""
var robotId: String = ""
var listItemId: Int = 0
var conversationId: String = ""
var messages:[YHAIChatMessage] = []
var historyLastMessageId: String = ""
var isNeedShowBannerHeader: Bool = false
var robotType: Int = 0
var robotType: String = ""
let manager = YHAIRequestManager()
let viewModel = YHAIViewModel()
......@@ -178,17 +179,7 @@ class YHAIRobotChatViewController: YHBaseViewController {
var results = self.viewModel.historyMessages.map {
return $0.convertToChatMessage()
}
if !isPull {
if results.count <= 0 { // 无聊天记录时隐藏下拉组件
tableView.header = nil
} else {
tableView.es.addYHPullToRefresh {
self.getHistoryMessages(true)
}
}
}
let uuids = messages.map {
return $0.messageId
}
......@@ -215,11 +206,11 @@ class YHAIRobotChatViewController: YHBaseViewController {
YHCommonAlertView.show("删除历史记录", "删除后记录无法恢复", "取消", "删除", fullGuestureEnable: false) {
} callBack: {
self.manager.clearLobalSubRobotConversationIdForRobotId(self.robotId)
self.manager.clearLobalSubRobotConversationIdForListItemId("\(self.listItemId)")
self.viewModel.requestConversationId { sessionId in
if !sessionId.isEmpty {
self.conversationId = sessionId
self.manager.saveLobalSubRobotConversationId(sessionId, robotId: self.robotId)
self.manager.saveLobalSubRobotConversationId(sessionId, listItemId: "\(self.listItemId)")
self.messages.removeAll()
self.tableView.reloadData()
YHHUD.flash(message: "清除成功")
......@@ -230,15 +221,38 @@ class YHAIRobotChatViewController: YHBaseViewController {
func getFlowMessages() -> [String] {
if robotType == YHAYRobotType.education.rawValue {
if robotType == YHAIRobotType.education.rawValue {
return ["香港教育有哪些优势?", "去香港读书有哪些条件?", "申请香港学校费用是多少?", "了解银河教育插班服务流程", "了解银河教育插班录取率"]
} else if robotType == YHAYRobotType.sale.rawValue {
} else if robotType == YHAIRobotType.sale.rawValue {
return ["优才学历加分要求是什么?", "优才现在要怎么申请?", "墨尔本大学是优才合资格大学吗?", "推荐一些优才产品"]
}
return []
}
func getHeaderTitle() -> String {
if robotType == YHAIRobotType.education.rawValue {
return "Hello,我是香港教育宝"
} else if robotType == YHAIRobotType.sale.rawValue {
return "Hello,我是新港生活规划师"
}
return ""
}
func getHeaderDesc() -> String {
if robotType == YHAIRobotType.education.rawValue {
return "有香港教育的问题尽管问我"
} else if robotType == YHAIRobotType.sale.rawValue {
return "香港身份办理问题可以找我"
}
return ""
}
}
extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSource {
......@@ -344,6 +358,8 @@ extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSourc
}
let view = YHAIChatBannerView()
view.titleLabel.text = getHeaderTitle()
view.descLabel.text = getHeaderDesc()
view.bannerArr = self.getBannerForRobotType(robotType)
view.messages = getFlowMessages()
view.selectFlowMsgBlock = {
......@@ -367,14 +383,15 @@ extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSourc
return view
}
func getBannerForRobotType(_ robotType: Int) -> [YHAIChatBannerItem] {
if robotType == YHAYRobotType.sale.rawValue {
func getBannerForRobotType(_ robotType: String) -> [YHAIChatBannerItem] {
if robotType == YHAIRobotType.sale.rawValue {
return [YHAIChatBannerItem(id: 0, title: "了解银河集团", desc: "香港身份生活一站式服务平台", msg: "银河集团,能够为我提供什么?"),
YHAIChatBannerItem(id: 1, title: "香港身份智能评估", desc: "60s快速评估,了解自身条件是否符合", msg: "开始身份办理评估"),
YHAIChatBannerItem(id: 2, title: "银河产品矩阵", desc: "香港身份、生活多样产品", msg: "介绍一下银河的产品"),]
}
if robotType == YHAYRobotType.education.rawValue {
if robotType == YHAIRobotType.education.rawValue {
return [YHAIChatBannerItem(id: 0, title: "幼中小学升学", desc: "去香港插班需要考核哪些"),
YHAIChatBannerItem(id: 1, title: "大学升学", desc: "DSE分数和Alevel的换算关系"),
YHAIChatBannerItem(id: 2, title: "银河教育服务", desc: "银河教育插班成功率如何?"),]
......
......@@ -150,7 +150,7 @@ extension YHAIServiceListViewController: UICollectionViewDelegate, UICollectionV
}
} else if model.redirectMode == 2 {
// customerVoice -> APP客户心声 productList -> APP-首页银河甄选
// customerVoice -> APP客户心声 productList -> APP-首页银河甄选 AppServiceTab -> 服务页
if model.redirectPath == YHAIJumpPageType.customerHeart.rawValue {
//客户心声
let vc = YHOtherServiceViewController()
......@@ -162,19 +162,32 @@ extension YHAIServiceListViewController: UICollectionViewDelegate, UICollectionV
let vc = YHSelectViewController()
vc.hideFlag = false
UIViewController.current?.navigationController?.pushViewController(vc, animated: true)
} else if model.redirectPath == YHAIJumpPageType.appServiceTab.rawValue {
// 服务页
UIViewController.current?.navigationController?.popToRootViewController(animated: true)
goTabBarBy(tabType: .service)
}
} else if model.redirectMode == 3 { // agent
self.manager.getSubRobotChatConversationId(robotId: model.redirectPath) {
sesseionId in
let vc = YHAIRobotChatViewController()
vc.isNeedShowBannerHeader = model.isNeedShowBannerHeader()
vc.myTitle = model.title
vc.robotId = model.redirectPath
vc.conversationId = sesseionId
vc.robotType = model.botType
self.navigationController?.pushViewController(vc, animated: true)
if model.businessType == YHAIRobotType.main.rawValue {
// 切到主Robot
NotificationCenter.default.post(name: YhConstant.YhNotification.didSwitchToAIChatNotification, object: nil)
} else {
self.manager.getSubRobotChatConversationId(listItemId: "\(model.id)") {
sesseionId in
let vc = YHAIRobotChatViewController()
vc.isNeedShowBannerHeader = model.isNeedShowBannerHeader()
vc.myTitle = model.title
vc.robotId = model.redirectPath
vc.listItemId = model.id
vc.conversationId = sesseionId
vc.robotType = model.businessType
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
}
......
......@@ -19,10 +19,10 @@ class YHAIListModel: SmartCodable {
}
}
enum YHAYRobotType: Int {
case house = 1
case education = 2
case sale = 3
enum YHAIRobotType: String {
case main = "mainBot"
case education = "educationBot"
case sale = "saleBot"
}
class YHEntranceconfigModel: SmartCodable {
......@@ -34,11 +34,11 @@ class YHEntranceconfigModel: SmartCodable {
var description: String = ""
var btnText: String = ""
var redirectMode: Int = 0
var botType: Int = 0 // 1:房产 2:教育 3:销售
var businessType: String = ""
var redirectPath: String = ""
func isNeedShowBannerHeader() -> Bool {
if botType == YHAYRobotType.education.rawValue || botType == YHAYRobotType.sale.rawValue {
if businessType == YHAIRobotType.education.rawValue || businessType == YHAIRobotType.sale.rawValue {
return true
}
return false
......
......@@ -229,8 +229,10 @@ class YHAIMessageBody: SmartCodable {
}
enum YHAIJumpPageType: String {
// customerVoice -> APP客户心声 productList -> APP-首页银河甄选 AppServiceTab -> 服务页
case customerHeart = "customerVoice" // APP客户心声
case galaxySelect = "productList" // APP-首页银河甄选
case appServiceTab = "AppServiceTab" // 服务页
}
class YHAIListInfoModel: SmartCodable {
......
......@@ -26,6 +26,7 @@ class YHAIChatBannerItemCell: FSPagerViewCell {
visualEffectView.colorTint = UIColor(hex: 0xAFAFAF).withAlphaComponent(0.15)
visualEffectView.blurRadius = 16
visualEffectView.scale = 1
visualEffectView.isHidden = true
return visualEffectView
}()
......
......@@ -37,27 +37,28 @@ class YHAIChatBannerView: UIView {
}
}
lazy var bgImgV: UIImageView = {
let imagV : UIImageView = UIImageView()
imagV.contentMode = .scaleAspectFill
imagV.clipsToBounds = true
imagV.image = UIImage(named: "ai_chat_header_bg")
return imagV
lazy var titleLabel: UILabel = {
let lable = UILabel()
lable.font = UIFont.PFSC_R(ofSize: 12)
lable.textColor = UIColor.init(hex: 0x6D788A)
lable.text = "Hello,我是新港生活规划师"
return lable
}()
lazy var rolerImgV: UIImageView = {
let imagV : UIImageView = UIImageView()
imagV.contentMode = .scaleAspectFill
imagV.clipsToBounds = true
imagV.image = UIImage(named: "ai_chat_header_role")
return imagV
lazy var descLabel: UILabel = {
let lable = UILabel()
lable.font = UIFont.PFSC_B(ofSize: 15)
lable.textColor = UIColor.mainTextColor
lable.text = "需要香港身份办理找我~"
return lable
}()
lazy var arrowImgV: UIImageView = {
lazy var bgImgV: UIImageView = {
let imagV : UIImageView = UIImageView()
imagV.contentMode = .scaleAspectFill
imagV.clipsToBounds = true
imagV.image = UIImage(named: "ai_chat_heaer_banner_arrow")
imagV.image = UIImage(named: "ai_chat_header_bg")
imagV.isUserInteractionEnabled = true
return imagV
}()
......@@ -130,10 +131,10 @@ class YHAIChatBannerView: UIView {
func createUI() {
addSubview(bgImgV)
addSubview(rolerImgV)
addSubview(bannerView)
bgImgV.addSubview(titleLabel)
bgImgV.addSubview(descLabel)
bgImgV.addSubview(bannerView)
bannerView.addSubview(indicatorView)
bannerView.addSubview(arrowImgV)
bgImgV.snp.makeConstraints { make in
make.top.equalTo(0)
......@@ -142,34 +143,32 @@ class YHAIChatBannerView: UIView {
make.height.equalTo(242)
}
rolerImgV.snp.makeConstraints { make in
make.top.equalTo(0)
make.right.equalTo(-10)
make.width.equalTo(200)
make.height.equalTo(180)
titleLabel.snp.makeConstraints { make in
make.top.equalTo(57)
make.left.equalTo(20)
make.height.equalTo(20)
}
descLabel.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom)
make.left.equalTo(20)
make.height.equalTo(24)
}
bannerView.snp.makeConstraints { make in
make.bottom.equalTo(bgImgV)
make.left.equalTo(22)
make.right.equalTo(-22)
make.left.equalTo(0)
make.right.equalTo(0)
make.height.equalTo(YHAIChatBannerView.bannersHeight)
}
indicatorView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.height.equalTo(2)
make.bottom.equalTo(-14)
make.bottom.equalTo(-16)
}
bannerView.reloadData()
arrowImgV.snp.makeConstraints { make in
make.right.equalTo(-20)
make.width.equalTo(12)
make.height.equalTo(4)
make.bottom.equalTo(-40)
}
addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.left.equalTo(20)
......
......@@ -17,9 +17,9 @@ class YHAIProductCell: UICollectionViewCell {
titleLabel.text = model.title
subtitleLabel.text = model.description
if !model.btnText.isEmpty {
blackButton.setTitle(model.btnText, for: .normal)
blackTitleLabel.text = model.btnText
} else {
blackButton.setTitle("去了解", for: .normal)
blackTitleLabel.text = "去了解"
}
bgImgView.sd_setImage(with: URL(string: model.cover), placeholderImage: UIImage(named: "ai_product_bg_default"))
}
......@@ -48,22 +48,31 @@ class YHAIProductCell: UICollectionViewCell {
return lable
}()
lazy var blackButton: UIButton = {
let btn = UIButton(type: .custom)
btn.setTitle("去了解", for: .normal)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = .PFSC_M(ofSize: 11)
btn.backgroundColor = .black
btn.addTarget(self, action: #selector(didSendButtonClicked), for: .touchUpInside)
btn.YH_clickEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
btn.layer.cornerRadius = 13.0
btn.clipsToBounds = true
return btn
lazy var blackView: UIView = {
let v = UIView()
v.backgroundColor = .black
v.layer.cornerRadius = 13.0
v.clipsToBounds = true
v.layer.allowsEdgeAntialiasing = true
v.addSubview(blackTitleLabel)
blackTitleLabel.snp.makeConstraints { make in
make.left.equalTo(10)
make.right.equalTo(-10)
make.top.bottom.equalToSuperview()
}
return v
}()
@objc func didSendButtonClicked() {
}
lazy var blackTitleLabel: UILabel = {
let lable = UILabel()
lable.textColor = UIColor.white
lable.textAlignment = .left
lable.font = UIFont.PFSC_M(ofSize: 11)
lable.text = "去了解"
return lable
}()
required init?(coder: NSCoder) {
super.init(coder: coder)
......@@ -82,7 +91,7 @@ class YHAIProductCell: UICollectionViewCell {
contentView.addSubview(bgImgView)
bgImgView.addSubview(titleLabel)
bgImgView.addSubview(subtitleLabel)
bgImgView.addSubview(blackButton)
bgImgView.addSubview(blackView)
bgImgView.snp.makeConstraints { make in
make.left.equalTo(0)
......@@ -101,14 +110,14 @@ class YHAIProductCell: UICollectionViewCell {
subtitleLabel.snp.makeConstraints { make in
make.left.equalTo(22)
make.right.equalTo(-22)
make.bottom.equalTo(blackButton.snp.top).offset(-12)
make.bottom.equalTo(blackView.snp.top).offset(-12)
}
blackButton.snp.makeConstraints { make in
blackView.snp.makeConstraints { make in
make.left.equalTo(22)
make.bottom.equalTo(-24)
make.height.equalTo(26)
make.width.equalTo(54)
make.right.lessThanOrEqualTo(-22)
}
}
......
......@@ -48,7 +48,13 @@ class YHAIJumpPageTool {
let vc = YHSelectViewController()
vc.hideFlag = false
UIViewController.current?.navigationController?.pushViewController(vc, animated: true)
} else if path == YHAIJumpPageType.appServiceTab.rawValue {
// 服务页
UIViewController.current?.navigationController?.popToRootViewController(animated: true)
goTabBarBy(tabType: .service)
}
} else if mode == 3 {
}
......
......@@ -28,10 +28,15 @@ class YHHomePageViewController: YHBaseViewController {
var homeHeaderViewHeight : CGFloat = 0.0
private var isGoYHManagerFlag : Bool = false
private var didShowFloating : Bool = false
var configModel: YHHomeInfoModel? {
didSet {
homeHeaderView.kingModel = configModel?.king_kong_list ?? []
if let live_info = configModel?.live_info {
setupLiveFloating(liveInfo: live_info)
}
}
}
......@@ -112,6 +117,16 @@ class YHHomePageViewController: YHBaseViewController {
//MARK: - 私有方法
private extension YHHomePageViewController {
private func setupLiveFloating(liveInfo: YHHomeLiveInfoModel) {
printLog("#####setupLiveFloating(liveInfo: YHHomeLiveInfoModel) ")
guard !didShowFloating else {
return
}
didShowFloating = true
let playbackInfo = YHPlayerManager.PlaybackInfo(id: liveInfo.id, token: liveInfo.token, channelId: liveInfo.rtmp_channel, uid: UInt(liveInfo.id), isLive: true, scene: .floating, playerType: .main)
YHPlayerManager.shared.enterFloating(from: nil, playbackInfo: playbackInfo)
}
func gotoMgrVC() {
if self.isGoYHManagerFlag {
return
......
......@@ -288,7 +288,7 @@ extension YHLifeViewController: UICollectionViewDelegate, UICollectionViewDataSo
return
}
let item = self.viewModel.liveArr[indexPath.row]
let playbackInfo = YHPlayerManager.PlaybackInfo(id: item.id, url: item.pull_url, title: item.live_title, roomId: item.room_id, uid: UInt(item.id), isLive: true, scene: .fullscreen)
let playbackInfo = YHPlayerManager.PlaybackInfo(id: item.id, url: nil, title: item.live_title, roomId: item.room_id, uid: UInt(item.id), isLive: true, scene: .fullscreen)
YHPlayerManager.shared.enterLive(from: nil, playbackInfo: playbackInfo)
}
}
......
......@@ -24,7 +24,7 @@ class YHBannerModel: SmartCodable {
var live_type: Int = 0 // 播放类型:1 展示最新直播,2指定直播,3 指定录播
var live_pull_url: String = "" //直播拉流链接
var video_url: String = "" //视频链接
var recorded_cate_id: Int = 0 //录播分类ID
var recorded_video_id: Int = 0 //录播分类ID
var app_id: String = "" //声网直播
var token: String = "" //声网直播
var rtmp_channel: String = "" //声网直播
......
......@@ -57,7 +57,10 @@ class YHHomeTabInfoModel : SmartCodable {
class YHHomeLiveInfoModel : SmartCodable {
var id: Int = 0
var status: Int = 0
var pull_url: Int = 0
var app_id: String = ""
var token: String = ""
var rtmp_channel: String = ""
var img_url: String = ""
required init() {
}
}
......@@ -25,11 +25,8 @@ class YHHomeBannerView: UIView {
// 指定显示图片为第一个
bannerView.selectItem(at: 0, animated: false)
let noNeedAutoScroll = dataArr.contains(where: {
$0.skip_type == 100 || $0.skip_type == 102
$0.skip_type == 100
})
// // TODO: - alex测试
// noNeedAutoScroll = true
// // TODO: - alex测试
// 开启定时器开始滚动
bannerView.automaticSlidingInterval = noNeedAutoScroll ? 0 : bannerSildingInterval
bannerView.removesInfiniteLoopForSingleItem = true
......@@ -146,14 +143,6 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
if model.isLocalItemFlag {
return
}
// // TODO: - alex测试
// if index == 1 {
// let cell: YHHomeBannerCollectionViewCell? = pagerView.cellForItem(at: index) as? YHHomeBannerCollectionViewCell
// let playbackInfo = YHPlayerManager.PlaybackInfo(id: 40, url: "https://pull-flv-f1-admin.douyincdn.com/thirdgame/stream-7443723341506054922_md.flv?keeptime=00093a80&wsSecret=cf2c048a5bceb7669e37b229e807c0e2&wsTime=674d82e7&major_anchor_level=common&abr_pts=-800&select_mode=score&_session_id=037-2024120217503167B9DF9F03DF6401DE50.1733133032147.26047&rsi=1", title: nil, roomId: nil, isLive: true, scene: .fullscreen)
// YHPlayerManager.shared.enterLive(from: cell?.bannerImagV, playbackInfo: playbackInfo)
// return
// }
// // TODO: - alex测试
switch model.skip_type {
case 1: //跳转H5
if model.skip_url.isEmpty == false {
......@@ -244,11 +233,15 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
// live_type 播放类型:1 展示最新直播,2指定直播,3 指定录播
// live_pull_url 直播拉流链接
// video_url 视频链接
// recorded_cate_id 录播分类id token: String? = nil, channelId
// recorded_video_id 录播id token: String? = nil, channelId
if !checkLogin() {
return
}
let cell: YHHomeBannerCollectionViewCell? = pagerView.cellForItem(at: index) as? YHHomeBannerCollectionViewCell
if let player = YHPlayerManager.shared.getPlayer(.secondary) {
YHPlayerManager.shared.leaveChannel(for: player)
player.clearPlayerView()
}
let playbackInfo = YHPlayerManager.PlaybackInfo(id: model.live_id, url: model.live_pull_url, title: nil, roomId: nil, token: model.token, channelId: model.rtmp_channel, uid: UInt(model.live_id), isLive: true, scene: .fullscreen)
YHPlayerManager.shared.enterLive(from: cell?.bannerImagV, playbackInfo: playbackInfo)
printLog("跳转直播")
......@@ -257,14 +250,22 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
if !checkLogin() {
return
}
if let player = YHPlayerManager.shared.getPlayer(.secondary) {
player.stop()
player.clearPlayerView()
}
let cell: YHHomeBannerCollectionViewCell? = pagerView.cellForItem(at: index) as? YHHomeBannerCollectionViewCell
let playbackInfo = YHPlayerManager.PlaybackInfo(id: model.live_id, url: model.video_url, title: nil, roomId: nil, uid: nil, isLive: false, scene: .fullscreen)
let playbackInfo = YHPlayerManager.PlaybackInfo(id: model.recorded_video_id, url: model.video_url, title: nil, roomId: nil, uid: nil, isLive: false, scene: .fullscreen)
YHPlayerManager.shared.enterVOD(from: cell?.bannerImagV, playbackInfo: playbackInfo)
case 102://图片直播
printLog("跳转录播")
if !checkLogin() {
return
}
if let player = YHPlayerManager.shared.getPlayer(.secondary) {
YHPlayerManager.shared.leaveChannel(for: player)
player.clearPlayerView()
}
let cell: YHHomeBannerCollectionViewCell? = pagerView.cellForItem(at: index) as? YHHomeBannerCollectionViewCell
let playbackInfo = YHPlayerManager.PlaybackInfo(id: model.live_id, url: model.live_pull_url, title: nil, roomId: nil, token: model.token, channelId: model.rtmp_channel, uid: UInt(model.live_id), isLive: true, scene: .fullscreen)
YHPlayerManager.shared.enterLive(from: cell?.bannerImagV, playbackInfo: playbackInfo)
......@@ -294,32 +295,17 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
if model.isLocalItemFlag {
return
}
// // TODO: - alex测试
// if let cell = cell as? YHHomeBannerCollectionViewCell {
// if index == 1 {
//
// let playbackInfo = YHPlayerManager.PlaybackInfo(id: 40, url: "https://pull-flv-f1-admin.douyincdn.com/thirdgame/stream-7443723341506054922_md.flv?keeptime=00093a80&wsSecret=cf2c048a5bceb7669e37b229e807c0e2&wsTime=674d82e7&major_anchor_level=common&abr_pts=-800&select_mode=score&_session_id=037-2024120217503167B9DF9F03DF6401DE50.1733133032147.26047&rsi=1", title: nil, roomId: nil, isLive: true, scene: .banner)
// YHPlayerManager.shared.enterBanner(playbackInfo: playbackInfo, inView: cell.bannerImagV)
//
// } else {
// let player = YHPlayerManager.shared.getPlayer(.secondary)
// player?.setPlayView(nil)
// }
// return
// }
// // TODO: - alex测试
if model.skip_url.isEmpty == false {
if let cell = cell as? YHHomeBannerCollectionViewCell {
if model.skip_type == 100 || model.skip_type == 102 {
YHPlayerManager.shared.play(url: model.live_pull_url, inView: cell.bannerImagV, title: nil, type: .secondary)
let playbackInfo = YHPlayerManager.PlaybackInfo(id: model.live_id, url: model.live_pull_url, title: nil, roomId: nil, isLive: true, scene: .banner)
YHPlayerManager.shared.enterBanner(playbackInfo: playbackInfo, inView: cell.bannerImagV)
} else {
let player = YHPlayerManager.shared.getPlayer(.secondary)
player?.setPlayView(nil)
if let cell = cell as? YHHomeBannerCollectionViewCell {
if model.skip_type == 100 {
let playbackInfo = YHPlayerManager.PlaybackInfo(id: model.live_id, token: model.token, channelId: model.rtmp_channel, uid: UInt(model.live_id), isLive: true, scene: .banner, playerType: .secondary)
YHPlayerManager.shared.enterBanner(playbackInfo: playbackInfo, inView: cell.bannerImagV)
} else {
if let player = YHPlayerManager.shared.getPlayer(.secondary) {
YHPlayerManager.shared.leaveChannel(for: player)
player.clearPlayerView()
}
}
}
}
......@@ -331,15 +317,13 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
if model.isLocalItemFlag {
return
}
// // TODO: - alex测试
// if index == 1 {
// YHPlayerManager.shared.stop(type: .secondary)
// return
// }
// // TODO: - alex测试
if model.skip_url.isEmpty == false {
if model.skip_type == 100 || model.skip_type == 102 {
YHPlayerManager.shared.stop(type: .secondary)
if let player = YHPlayerManager.shared.getPlayer(.secondary) {
YHPlayerManager.shared.leaveChannel(for: player)
player.clearPlayerView()
}
}
}
}
......
......@@ -118,7 +118,7 @@ extension YHSelectLookView: UICollectionViewDelegate, UICollectionViewDataSource
return
}
let item = items[indexPath.row]
let playbackInfo = YHPlayerManager.PlaybackInfo(id: item.id, url: item.pull_url, title: item.live_title, roomId: item.room_id, uid: UInt(item.id), isLive: true, scene: .fullscreen)
let playbackInfo = YHPlayerManager.PlaybackInfo(id: item.id, url: nil, title: item.live_title, roomId: item.room_id, uid: UInt(item.id), isLive: true, scene: .fullscreen)
YHPlayerManager.shared.enterLive(from: nil, playbackInfo: playbackInfo)
}
}
......
......@@ -626,7 +626,7 @@ extension YHJsApi {
let playbackInfo = YHPlayerManager.PlaybackInfo(id: id.intValue(), isLive: true)
YHPlayerManager.shared.enterLive(from: nil, playbackInfo: playbackInfo)
} else if type == 2 {
let playbackInfo = YHPlayerManager.PlaybackInfo(id: id.intValue(), isLive: false)
let playbackInfo = YHPlayerManager.PlaybackInfo(id: id.intValue(), isLive: false, needHideZoomButton: true)
YHPlayerManager.shared.enterVOD(from: nil, playbackInfo: playbackInfo)
}
......
......@@ -184,10 +184,10 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
playbackInfo?.channelId = liveDetail.rtmp_channel
playbackInfo?.token = liveDetail.token
playbackInfo?.title = liveDetail.live_title
messageListView.anchorName = liveDetail.hxNickname
if needJoinLiveChannel {
if !liveDetail.rtmp_channel.isEmpty, !liveDetail.token.isEmpty, let uid = playbackInfo?.uid, let player = player {
player.setPlayView(playerView)
YHPlayerManager.shared.joinChannel(for: player, token: liveDetail.token, channelId: liveDetail.rtmp_channel, uid: uid)
YHPlayerManager.shared.joinChannel(for: player, token: liveDetail.token, channelId: liveDetail.rtmp_channel, uid: uid, view: playerView)
} else if currentPlayingURL == nil, !liveDetail.pullUrl.isEmpty {
play(url: liveDetail.pullUrl) // 如果没有预设URL,使用接口返回的URL播放
}
......@@ -245,7 +245,6 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
func play(url: String, title: String? = nil) {
currentPlayingURL = url
currentVideoTitle = title
// controlView.setTitle(title ?? "")
YHPlayerManager.shared.play(url: url, inView: playerView, title: title)
}
......
......@@ -16,7 +16,7 @@ class YHMainChannelDelegate: NSObject, AgoraRtcEngineDelegate {
weak var player: YHPlayer?
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
printLog("main channel: local user join room \(channelId ?? ""): \(uid) \(elapsed)ms")
printLog("$$$$###main channel: local user join room \(channelId ?? ""): \(uid) \(elapsed)ms")
}
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
......@@ -24,7 +24,7 @@ class YHMainChannelDelegate: NSObject, AgoraRtcEngineDelegate {
let player = player,
let view = player.currentPlayView else { return }
printLog("main channel: remote user join room \(channelId): \(uid) \(elapsed)ms")
printLog("$$$$###main channel: remote user join room \(channelId): \(uid) \(elapsed)ms")
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = uid
......@@ -34,7 +34,7 @@ class YHMainChannelDelegate: NSObject, AgoraRtcEngineDelegate {
}
func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
printLog("main channel: remote user left: \(uid) reason \(reason)")
printLog("$$$$###main channel: remote user left: \(uid) reason \(reason)")
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = uid
......@@ -42,6 +42,17 @@ class YHMainChannelDelegate: NSObject, AgoraRtcEngineDelegate {
videoCanvas.renderMode = .hidden
engine.setupRemoteVideo(videoCanvas)
}
func rtcEngine(_ engine: AgoraRtcEngineKit, videoSizeChangedOf sourceType: AgoraVideoSourceType, uid: UInt, size: CGSize, rotation: Int) {
printLog("$$$$###videoSizeChangedOf: \(size)")
if let player = player {
player.delegate?.player(player, didReceiveVideoSize: size)
}
}
func rtcEngine(_ engine: AgoraRtcEngineKit, firstRemoteVideoFrameOfUid uid: UInt, size: CGSize, elapsed: Int) {
printLog("$$$$###firstRemoteVideoFrameOfUid: \(size)")
}
}
class YHSecondaryChannelDelegate: NSObject, AgoraRtcEngineDelegate {
......@@ -86,4 +97,15 @@ class YHSecondaryChannelDelegate: NSObject, AgoraRtcEngineDelegate {
connection.localUid = player.currentUid ?? 0
engine.setupRemoteVideoEx(videoCanvas, connection: connection)
}
func rtcEngine(_ engine: AgoraRtcEngineKit, videoSizeChangedOf sourceType: AgoraVideoSourceType, uid: UInt, size: CGSize, rotation: Int) {
printLog("$$$$###videoSizeChangedOf: \(size)")
if let player = player {
player.delegate?.player(player, didReceiveVideoSize: size)
}
}
func rtcEngine(_ engine: AgoraRtcEngineKit, firstRemoteVideoFrameOfUid uid: UInt, size: CGSize, elapsed: Int) {
printLog("###firstRemoteVideoFrameOfUid: \(size)")
}
}
......@@ -15,6 +15,12 @@ enum YHPlayerType {
case secondary
}
// MARK: - 播放模式
enum YHPlayMode {
case live // 直播
case vod // 点播
}
protocol YHPlayerDelegate: AnyObject {
func player(_ player: YHPlayer, didChangedToState state: AgoraMediaPlayerState, reason: AgoraMediaPlayerReason)
func player(_ player: YHPlayer, didChangedToPosition position: Int)
......@@ -24,13 +30,7 @@ protocol YHPlayerDelegate: AnyObject {
// MARK: - 播放器实例封装
class YHPlayer {
// 持有一个固定的播放视图
private lazy var playerContentView: UIView = {
let view = UIView()
view.backgroundColor = .black
return view
}()
// MARK: - Properties
weak var delegate: YHPlayerDelegate?
let type: YHPlayerType
var playerKit: AgoraRtcMediaPlayerProtocol?
......@@ -43,15 +43,17 @@ class YHPlayer {
private(set) var currentToken: String?
private(set) var currentUid: UInt?
private(set) var isJoined: Bool = false
private(set) var playMode: YHPlayMode = .vod
// 保存最后的播放信息用于恢复
private var lastPlaybackInfo: (url: String?, title: String?)?
//private var lastPlaybackInfo: (url: String?, title: String?, mode: YHPlayMode)?
var isMuted: Bool {
get { playerKit?.getMute() ?? false }
set { playerKit?.mute(newValue) }
}
// MARK: - Initialization
init(type: YHPlayerType, playerKit: AgoraRtcMediaPlayerProtocol?, agoraKit: AgoraRtcEngineKit?) {
self.type = type
self.playerKit = playerKit
......@@ -60,51 +62,59 @@ class YHPlayer {
playerKit?.setLoopCount(-1)
}
// func setPlayViewNull() {
// let hideVideoCanvas = AgoraRtcVideoCanvas()
// hideVideoCanvas.uid = currentUid ?? 0
// hideVideoCanvas.view = nil
// hideVideoCanvas.renderMode = .hidden
// agoraKit?.setupRemoteVideo(hideVideoCanvas)
// }
func setPlayView(_ view: UIView?) {
// 保存旧的视图用于动画
// let oldView = currentPlayView
// 设置新的视图
// MARK: - View Management
func setPlayModeAndPlayView(_ mode: YHPlayMode, _ view: UIView?) {
playMode = mode
currentPlayView = view
playerKit?.setRenderMode(.fit)
// if let view = view, let uid = currentUid, let channelId = currentChannelId {
// if type == .secondary {
// // 副播放器需要使用 Ex 方法
// let videoCanvas = AgoraRtcVideoCanvas()
// videoCanvas.view = view
// videoCanvas.renderMode = .fit
// videoCanvas.uid = uid
// let connection = AgoraRtcConnection()
// connection.channelId = channelId
// connection.localUid = uid
// agoraKit?.setupRemoteVideoEx(videoCanvas, connection: connection)
// } else {
// let hideVideoCanvas = AgoraRtcVideoCanvas()
// hideVideoCanvas.uid = uid
// hideVideoCanvas.view = nil
// hideVideoCanvas.renderMode = .hidden
// agoraKit?.setupRemoteVideo(hideVideoCanvas)
// DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
// // 主播放器使用普通方法
// let videoCanvas = AgoraRtcVideoCanvas()
// videoCanvas.view = view
// videoCanvas.renderMode = .fit
// videoCanvas.uid = uid
// self.agoraKit?.setupRemoteVideo(videoCanvas)
// })
//
// }
// }
if playMode == .vod {
// 点播模式:直接设置 MediaPlayer 的视图
playerKit?.setView(view)
} else {
// 直播模式:设置远程视图
if let view = view, let uid = currentUid {
setupRemoteVideo(view: view, uid: uid)
}
}
}
func clearPlayerView() {
currentPlayView = nil
if playMode == .vod {
playerKit?.setView(nil)
} else {
// 直播模式:设置远程视图
if let uid = currentUid {
setupRemoteVideo(view: nil, uid: uid)
}
}
}
private func setupRemoteVideo(view: UIView?, uid: UInt) {
if type == .secondary {
// 副播放器使用 Ex 方法
guard let channelId = currentChannelId else { return }
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.view = view
videoCanvas.renderMode = .fit
videoCanvas.uid = uid
let connection = AgoraRtcConnection()
connection.channelId = channelId
connection.localUid = uid
agoraKit?.setupRemoteVideoEx(videoCanvas, connection: connection)
} else {
// 主播放器使用普通方法
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.view = view
videoCanvas.renderMode = .fit
videoCanvas.uid = uid
agoraKit?.setupRemoteVideo(videoCanvas)
}
}
// MARK: - Channel Management
func setJoinInfo(token: String, channelId: String, uid: UInt) {
currentToken = token
currentChannelId = channelId
......@@ -113,6 +123,24 @@ class YHPlayer {
}
func clearJoinInfo() {
if let uid = currentUid {
// 清除远程视图
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = uid
videoCanvas.view = nil
videoCanvas.renderMode = .hidden
if type == .secondary {
guard let channelId = currentChannelId else { return }
let connection = AgoraRtcConnection()
connection.channelId = channelId
connection.localUid = uid
agoraKit?.setupRemoteVideoEx(videoCanvas, connection: connection)
} else {
agoraKit?.setupRemoteVideo(videoCanvas)
}
}
currentToken = nil
currentChannelId = nil
currentUid = nil
......@@ -122,70 +150,72 @@ class YHPlayer {
currentTitle = nil
}
func play(url: String, title: String? = nil, view: UIView?) {
// MARK: - Playback Control
func play(url: String, title: String? = nil, view: UIView?, isLive: Bool = false) {
currentURL = url
currentTitle = title
lastPlaybackInfo = (url, title) // 保存播放信息
currentPlayView = view
playerKit?.setRenderMode(.fit)
playerKit?.setView(view)
let mediaSource = AgoraMediaSource()
mediaSource.url = url
mediaSource.autoPlay = true
let result = playerKit?.open(with: mediaSource)
if result == 0 {
playerKit?.play()
setPlayModeAndPlayView(isLive ? .live : .vod, view)
if !isLive {
currentPlayView = view
playerKit?.setRenderMode(.fit)
playerKit?.setView(view)
let mediaSource = AgoraMediaSource()
mediaSource.url = url
mediaSource.autoPlay = true
let result = playerKit?.open(with: mediaSource)
if result == 0 {
playerKit?.play()
}
}
}
func stop() {
playerKit?.stop()
clearJoinInfo()
currentPlayView = nil
currentURL = nil
currentTitle = nil
// 保留 lastPlaybackInfo 用于恢复
if playMode == .vod {
playerKit?.stop()
} else {
clearJoinInfo()
}
}
func pause() {
playerKit?.pause()
if playMode == .vod {
playerKit?.pause()
}
}
func resume() {
if playerKit?.getPlayerState() == .paused {
playerKit?.resume()
} else {
playerKit?.play()
if playMode == .vod {
if playerKit?.getPlayerState() == .paused {
playerKit?.resume()
} else {
playerKit?.play()
}
}
}
func resume(withNewView view: UIView) {
// 设置新的播放视图
//setPlayView(view)
// 如果有频道信息,通知 Manager 重新加入频道
if let token = currentToken,
let channelId = currentChannelId,
let uid = currentUid {
YHPlayerManager.shared.joinChannel(for: self,
token: token,
channelId: channelId,
uid: uid)
uid: uid, view: view)
}
// 如果有上次的播放信息,重新播放
if let lastInfo = lastPlaybackInfo,
let url = lastInfo.url {
play(url: url, title: lastInfo.title, view: view)
if let url = currentURL {
play(url: url,
title: currentTitle,
view: view,
isLive: playMode == .live)
}
}
// MARK: - Resource Management
func reset() {
stop()
setPlayView(nil)
setPlayModeAndPlayView(playMode, nil)
delegate = nil
lastPlaybackInfo = nil
}
func releasePlayer() {
......@@ -193,36 +223,35 @@ class YHPlayer {
playerKit = nil
}
// MARK: - Playback Info
// MARK: - Playback Info & Control
func getPosition() -> Int {
return playerKit?.getPosition() ?? 0
return playMode == .vod ? (playerKit?.getPosition() ?? 0) : 0
}
func getDuration() -> Int {
return playerKit?.getDuration() ?? 0
return playMode == .vod ? (playerKit?.getDuration() ?? 0) : 0
}
func getPlayState() -> AgoraMediaPlayerState {
return playerKit?.getPlayerState() ?? .idle
return playMode == .vod ? (playerKit?.getPlayerState() ?? .idle) : .playing
}
// MARK: - Seeking
func seek(to position: Int) {
playerKit?.seek(toPosition: position)
if playMode == .vod {
playerKit?.seek(toPosition: position)
}
}
// MARK: - Volume Control
func setVolume(_ volume: Int32) {
playerKit?.adjustPlayoutVolume(volume)
if playMode == .vod {
playerKit?.adjustPlayoutVolume(volume)
}
}
// MARK: - Playback Speed
func setPlaybackSpeed(_ speed: Double) {
playerKit?.setPlaybackSpeed(Int32(speed * 100))
if playMode == .vod {
playerKit?.setPlaybackSpeed(Int32(speed * 100))
}
}
}
......
......@@ -31,6 +31,7 @@ class YHPlayerManager: NSObject {
let isLive: Bool
var scene: PlaybackScene
var playerType: YHPlayerType
var needHideZoomButton: Bool = false
init(id: Int,
url: String? = nil,
......@@ -40,7 +41,9 @@ class YHPlayerManager: NSObject {
channelId: String? = nil,
uid: UInt? = nil,
isLive: Bool,
scene: PlaybackScene = .fullscreen) {
scene: PlaybackScene = .fullscreen,
playerType: YHPlayerType = .main,
needHideZoomButton: Bool = false) {
self.id = id
self.url = url
self.title = title
......@@ -50,7 +53,8 @@ class YHPlayerManager: NSObject {
self.uid = uid
self.isLive = isLive
self.scene = scene
playerType = .main
self.playerType = playerType
self.needHideZoomButton = needHideZoomButton
}
static func == (lhs: Self, rhs: Self) -> Bool {
......@@ -101,12 +105,9 @@ class YHPlayerManager: NSObject {
// MARK: - Basic Playback Control
func play(url: String, inView view: UIView? = nil, title: String? = nil, type: YHPlayerType = .main) {
func play(url: String, inView view: UIView? = nil, title: String? = nil, type: YHPlayerType = .main, isLive: Bool = false) {
let player = player(for: type)
if let view = view {
player.setPlayView(view)
}
player.play(url: url, title: title, view: view)
player.play(url: url, title: title, view: view, isLive: isLive)
}
func pause(type: YHPlayerType = .main) {
......@@ -121,10 +122,6 @@ class YHPlayerManager: NSObject {
activePlayers[type]?.stop()
}
func setPlayView(_ view: UIView?, type: YHPlayerType = .main) {
activePlayers[type]?.setPlayView(view)
}
func getCurrentPlayer(type: YHPlayerType = .main) -> YHPlayer? {
return activePlayers[type]
}
......@@ -208,13 +205,14 @@ class YHPlayerManager: NSObject {
func joinChannel(for player: YHPlayer,
token: String,
channelId: String,
uid: UInt) {
uid: UInt, view: UIView?) {
let option = AgoraRtcChannelMediaOptions()
option.publishCameraTrack = false
option.publishMicrophoneTrack = false
option.autoSubscribeVideo = true
option.autoSubscribeAudio = true
option.clientRoleType = .audience
player.setPlayModeAndPlayView(.live, view)
if player.type == .secondary {
let connection = AgoraRtcConnection()
connection.channelId = channelId
......@@ -290,13 +288,25 @@ class YHPlayerManager: NSObject {
playerVC.startPosition = startPosition
// 关闭小窗
exitFloating()
var needExitFloating = false
if floatingWindow != nil {
if sourceView != floatingWindow?.contentView {
needExitFloating = false
exitFloating()
} else {
needExitFloating = true
}
}
if let url = playbackInfo.url {
player.play(url: url, title: playbackInfo.title, view: playerVC.playerView)
}
present(navVC, from: sourceView)
if needExitFloating, floatingWindow != nil {
floatingWindow?.dismiss()
floatingWindow = nil
}
}
func enterLive(from sourceView: UIView?, playbackInfo: PlaybackInfo) {
......@@ -323,22 +333,56 @@ class YHPlayerManager: NSObject {
playerVC.playbackInfo = updatedInfo
// 关闭小窗
exitFloating()
var needExitFloating = false
if floatingWindow != nil {
if sourceView != floatingWindow?.contentView {
needExitFloating = false
exitFloating()
} else {
needExitFloating = true
}
}
// 加入频道
if let token = playbackInfo.token,
let channelId = playbackInfo.channelId, let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
joinChannel(for: player, token: token, channelId: channelId, uid: uid)
if player.isJoined {
leaveChannel(for: player)
joinChannel(for: player, token: token, channelId: channelId, uid: uid, view: playerVC.playerView)
// // 先清理上次的播放状态
// player.clearPlayerView()
// // 将播放状态转移到floating
// player.setPlayModeAndPlayView(.live, playerVC.playerView)
} else {
joinChannel(for: player, token: token, channelId: channelId, uid: uid, view: playerVC.playerView)
}
} else if let url = playbackInfo.url, url.count > 0 {
player.play(url: url, title: playbackInfo.title, view: playerVC.playerView)
}
present(navVC, from: sourceView)
if needExitFloating, floatingWindow != nil {
floatingWindow?.dismiss()
floatingWindow = nil
}
}
func enterFloating(from viewController: UIViewController? = nil, playbackInfo: PlaybackInfo) {
guard let window = UIApplication.shared.yhKeyWindow() else { return }
guard floatingWindow == nil else {
return
}
let isCurrentFromLive = viewController is YHLivePlayerViewController
let isCurrentFromVod = viewController is YHVODPlayerViewController
if !isCurrentFromLive, !isCurrentFromVod, let topVC = UIApplication.shared.yhKeyWindow()?.rootViewController?.topMostViewController() {
// 如果当前顶部控制器是播放器页面,则不创建浮窗
if topVC is YHLivePlayerViewController || topVC is YHVODPlayerViewController {
return
}
}
let playerType = determinePlayerType(for: .floating)
let player: YHPlayer = player(for: playerType)
......@@ -346,14 +390,6 @@ class YHPlayerManager: NSObject {
updatedInfo.scene = .floating
updatedInfo.playerType = playerType
currentPlaybackInfo[playerType] = updatedInfo
// // 加入频道
// if let token = playbackInfo.token,
// let channelId = playbackInfo.channelId, let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
// joinChannel(for: player, token: token, channelId: channelId, uid: uid)
// } else if let url = playbackInfo.url, !url.isEmpty {
// player.play(url: url, title: playbackInfo.title)
// }
// 获取当前播放视图的截图和位置
if let sourceView = player.currentPlayView,
......@@ -367,6 +403,7 @@ class YHPlayerManager: NSObject {
floatingWindow.playbackInfo = updatedInfo
floatingWindow.delegate = self
floatingWindow.player = player
player.delegate = floatingWindow
self.floatingWindow = floatingWindow
// 添加截图视图到窗口
......@@ -379,7 +416,17 @@ class YHPlayerManager: NSObject {
if let token = playbackInfo.token,
let channelId = playbackInfo.channelId,
let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
joinChannel(for: player, token: token, channelId: channelId, uid: uid)
if player.isJoined {
leaveChannel(for: player)
joinChannel(for: player, token: token, channelId: channelId, uid: uid, view: floatingWindow.contentView)
// // 先清理上次的播放状态
// player.clearPlayerView()
// // 将播放状态转移到floating
// player.setPlayModeAndPlayView(.live, floatingWindow.contentView)
} else {
joinChannel(for: player, token: token, channelId: channelId, uid: uid, view: floatingWindow.contentView)
}
} else if let url = playbackInfo.url, !url.isEmpty {
player.play(url: url, title: playbackInfo.title, view: floatingWindow.contentView)
}
......@@ -391,7 +438,7 @@ class YHPlayerManager: NSObject {
}, completion: { _ in
snapshotView.removeFromSuperview()
floatingWindow.show(in: window)
player.setPlayView(floatingWindow.contentView)
//player.setPlayView(floatingWindow.contentView)
})
}
......@@ -408,18 +455,20 @@ class YHPlayerManager: NSObject {
floatingWindow.player = player
floatingWindow.playbackInfo = updatedInfo
self.floatingWindow = floatingWindow
player.delegate = floatingWindow
// 检查是否存在当前播放器
if let token = playbackInfo.token,
let channelId = playbackInfo.channelId,
let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
joinChannel(for: player, token: token, channelId: channelId, uid: uid)
leaveChannel(for: player)
joinChannel(for: player, token: token, channelId: channelId, uid: uid, view: floatingWindow.contentView)
} else if let url = playbackInfo.url, !url.isEmpty {
player.play(url: url, title: playbackInfo.title, view: floatingWindow.contentView)
}
let showFloatingWindow = {
floatingWindow.show(in: window)
player.setPlayView(floatingWindow.contentView)
// player.setPlayView(floatingWindow.contentView)
}
if let viewController = viewController {
......@@ -440,18 +489,27 @@ class YHPlayerManager: NSObject {
currentPlaybackInfo[playerType] = updatedInfo
let player = player(for: playerType)
player.setPlayView(view)
// 加入频道
if let token = playbackInfo.token,
let channelId = playbackInfo.channelId, let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
joinChannel(for: player, token: token, channelId: channelId, uid: uid)
if player.isJoined {
leaveChannel(for: player)
joinChannel(for: player, token: token, channelId: channelId, uid: uid, view: view)
} else {
joinChannel(for: player, token: token, channelId: channelId, uid: uid, view: view)
}
} else if let url = playbackInfo.url, !url.isEmpty {
player.play(url: url, title: playbackInfo.title, view: view)
}
}
func exitFloating() {
// 直播退出channel
if floatingWindow?.playbackInfo?.isLive == true, let player = floatingWindow?.player {
leaveChannel(for: player)
} else {
floatingWindow?.player?.stop()
}
floatingWindow?.dismiss()
floatingWindow = nil
}
......@@ -505,7 +563,9 @@ extension YHPlayerManager: AgoraRtcMediaPlayerDelegate {
}
if floatingWindow?.player?.playerKit === playerKit {
floatingWindow?.setVideoSize(videoSize)
DispatchQueue.main.async {
self.floatingWindow?.setVideoSize(videoSize)
}
}
}
}
......
......@@ -42,7 +42,7 @@ class YHVODPlayerViewController: YHBasePlayerViewController {
func play(url: String, title: String? = nil) {
currentPlayingURL = url
currentVideoTitle = title
YHPlayerManager.shared.play(url: url, inView: playerView, title: title)
YHPlayerManager.shared.play(url: url, inView: playerView, title: title, isLive: false)
}
// MARK: - Lifecycle
......@@ -73,6 +73,7 @@ class YHVODPlayerViewController: YHBasePlayerViewController {
topBarView.shareButtonClickEvent = { [weak self] in
self?.shareLive()
}
topBarView.hideZoomButton(playbackInfo?.needHideZoomButton == true)
}
func enterFloating() {
......@@ -142,7 +143,7 @@ class YHVODPlayerViewController: YHBasePlayerViewController {
return
}
let item = detail.recordedVideoSlice[index]
self?.player?.seek(to: item.start_second * 1000)
self?.player?.seek(to: item.start_second * 1000 + 1)
}
// 时间改变回调
progressControl.onTimeChanged = { [weak self] time in
......
......@@ -14,7 +14,7 @@ class YHLiveDetailModel: SmartCodable {
var avatar: String = ""
var hxNickname: String = ""
var hxUid: String = ""
var access_num: Int = 0
var access_num: String = ""
var tips: String = ""
// 1:直播中 2:未直播 0:未知状态 3:结束直播
var status: Int = 0
......
......@@ -12,7 +12,7 @@ import SmartCodable
class YHRecordedDetailModel: SmartCodable {
var account: String = ""
var avatar: String = ""
var access_num: Int = 0
var access_num: String = ""
var tips: String = ""
var recorded_url: String = ""
var recorded_image: String = ""
......
......@@ -6,6 +6,7 @@
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import AgoraRtcKit
import UIKit
// MARK: - 浮窗管理
......@@ -187,15 +188,66 @@ class YHFloatingWindow: NSObject {
// MARK: - Private Methods
private func updateLayoutForOrientation() {
let currentWidth = containerView.bounds.width
let newHeight = currentWidth / videoOrientation.aspectRatio
UIView.animate(withDuration: 0.3) {
var frame = self.containerView.frame
frame.size.height = newHeight
self.containerView.frame = frame
self.delegate?.floatingWindow(self, didChangeSize: frame.size)
}
// let currentWidth = containerView.bounds.width
// let newHeight = currentWidth / videoOrientation.aspectRatio
//
// UIView.animate(withDuration: 0.3) {
// var frame = self.containerView.frame
// frame.size.height = newHeight
// self.containerView.frame = frame
// self.delegate?.floatingWindow(self, didChangeSize: frame.size)
// }
guard let window = UIApplication.shared.yhKeyWindow() else { return }
let currentWidth = containerView.bounds.width
let newHeight = currentWidth / videoOrientation.aspectRatio
// 计算安全区域
let safeAreaInsets = window.safeAreaInsets
let maxHeight = window.bounds.height - safeAreaInsets.top - safeAreaInsets.bottom
// 如果新高度超过最大高度,则反过来调整宽度
let finalWidth: CGFloat
let finalHeight: CGFloat
if newHeight > maxHeight {
finalHeight = maxHeight
finalWidth = maxHeight * videoOrientation.aspectRatio
} else {
finalWidth = currentWidth
finalHeight = newHeight
}
// 确保浮窗位置在屏幕范围内
var newFrame = containerView.frame
newFrame.size.width = finalWidth
newFrame.size.height = finalHeight
// 检查右边界
if newFrame.maxX > window.bounds.width - 16 {
newFrame.origin.x = window.bounds.width - newFrame.width - 16
}
// 检查左边界
if newFrame.minX < 16 {
newFrame.origin.x = 16
}
// 检查底部边界
if newFrame.maxY > window.bounds.height - 44 {
newFrame.origin.y = window.bounds.height - newFrame.height - 44
}
// 检查顶部边界
if newFrame.minY < safeAreaInsets.top + 16 {
newFrame.origin.y = safeAreaInsets.top + 16
}
UIView.animate(withDuration: 0.3) {
self.containerView.frame = newFrame
self.delegate?.floatingWindow(self, didChangeSize: newFrame.size)
}
}
// MARK: - Gesture Handlers
......@@ -439,3 +491,22 @@ extension YHFloatingWindow: UIGestureRecognizerDelegate {
return true
}
}
// MARK: - YHPlayerDelegate
extension YHFloatingWindow: YHPlayerDelegate {
func player(_ player: YHPlayer, didChangedToState state: AgoraMediaPlayerState, reason: AgoraMediaPlayerReason) {
//
}
func player(_ player: YHPlayer, didChangedToPosition position: Int) {
//
}
func player(_ player: YHPlayer, didReceiveVideoSize size: CGSize) {
DispatchQueue.main.async {
self.setVideoSize(size)
}
}
func player(_ player: YHPlayer, didChangedTo positionMs: Int, atTimestamp timestampMs: TimeInterval) {
//
}
}
......@@ -62,8 +62,8 @@ class YHLiveMessageCell: UITableViewCell {
}
}
func configureNormalMessage(_ nickname: String, _ content: String) {
let nickAtt = ASAttributedString(string: nickname + ":", .foreground(UIColor.white.withAlphaComponent(0.65)), .font(UIFont.PFSC_R(ofSize: 13)))
func configureNormalMessage(_ nickname: String, _ content: String, isAnchor: Bool) {
let nickAtt = isAnchor ? ASAttributedString(string: nickname + ":", .foreground(UIColor(hexString: "#FFE3BB") ?? UIColor.white), .font(UIFont.PFSC_R(ofSize: 13))) : ASAttributedString(string: nickname + ":", .foreground(UIColor.white.withAlphaComponent(0.65)), .font(UIFont.PFSC_R(ofSize: 13)))
let contentAtt = ASAttributedString(string: content, .foreground(UIColor.white), .font(UIFont.PFSC_R(ofSize: 13)))
contentLabel.attributed.text = nickAtt + contentAtt
}
......
......@@ -11,6 +11,7 @@ import UIKit
class YHLiveMessageListView: UIView {
private var messages: [EMChatMessage] = []
var anchorName: String?
// MARK: - UI Components
......@@ -107,7 +108,8 @@ extension YHLiveMessageListView: UITableViewDelegate, UITableViewDataSource {
printLog(body.text)
printLog("\(nickName) : \(body.text)")
content = body.text
cell.configureNormalMessage(nickName, content)
let isAnchor = nickName == anchorName
cell.configureNormalMessage(nickName, content, isAnchor: isAnchor)
} else if let body = message.body as? EMCustomMessageBody, body.event == YHChatRoomCustomLocal.tipsEvent, let customExt = body.customExt, let tips = customExt[YHChatRoomCustomLocal.tipsKey] {
cell.configureTipsMessage(tips)
}
......
......@@ -80,7 +80,7 @@ class YHPlayerTopBarView: UIView {
fatalError("init(coder:) has not been implemented")
}
func setupTopBarView(headUrl: String, nickname: String, count: Int) {
func setupTopBarView(headUrl: String, nickname: String, count: String) {
if let url = URL(string: headUrl) {
headPortrait.kf.setImage(with: url, placeholder: UIImage(named: "global_default_image"))
} else {
......@@ -90,9 +90,13 @@ class YHPlayerTopBarView: UIView {
updateCountLabel(count)
}
func updateCountLabel(_ count: Int) {
func updateCountLabel(_ count: String) {
infoCountLabel.text = "\(count)人来过"
}
func hideZoomButton(_ hide: Bool) {
zoomButton.isHidden = hide
}
}
extension YHPlayerTopBarView {
......
......@@ -187,6 +187,11 @@ func configTabBarController() -> YHTabBarViewController {
tabBarController.didHijackHandler = {
tabBarController, viewController, index in
if index == ai_tabIndex {
if !YHLoginManager.shared.isLogin() {
YHOneKeyLoginManager.shared.oneKeyLogin()
return
}
let vc = YHAITabViewController()
UIViewController.current?.navigationController?.pushViewController(vc)
}
......
......@@ -5,12 +5,12 @@
"scale" : "1x"
},
{
"filename" : "Mask group@2x.png",
"filename" : "卡片切图@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Mask group@3x.png",
"filename" : "卡片切图@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
......
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Rectangle 346242466@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Rectangle 346242466@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment