PQStuckPointMusicController.swift 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. //
  2. // PQStuckPointMusicController.swift
  3. // PQSpeed
  4. //
  5. // Created by SanW on 2021/4/28.
  6. // Copyright © 2021 BytesFlow. All rights reserved.
  7. //
  8. import AVFoundation
  9. import UIKit
  10. import Photos
  11. class PQStuckPointMusicController: PQBaseViewController {
  12. // 选中的总时长
  13. var selectedTotalDuration: Float64 = 0
  14. // 选择的总数
  15. var selectedDataCount: Int = 0
  16. // 选择的图片总数
  17. var selectedImageDataCount: Int = 0
  18. weak var stuckPointEditVC : PQStuckPointEditerController?
  19. var firstFrameImage:UIImage?
  20. // 选中的素材数据
  21. var selectedPhotoData: [PQEditVisionTrackMaterialsModel]?{
  22. didSet {
  23. if selectedPhotoData != nil && selectedPhotoData!.count > 0 {
  24. let photo = selectedPhotoData!.first!
  25. let option = PHImageRequestOptions()
  26. option.isNetworkAccessAllowed = true //允许下载iCloud的图片
  27. option.resizeMode = .none
  28. option.deliveryMode = .highQualityFormat
  29. let startTime = Date()
  30. PHImageManager.default().requestImage(for: photo.asset!,
  31. targetSize: CGSize(width: 1920, height: 1920),
  32. contentMode: .aspectFit,
  33. options: option)
  34. { (image, nil) in
  35. //image就是图片
  36. if image != nil {
  37. self.firstFrameImage = image
  38. if self.stuckPointEditVC != nil {
  39. self.stuckPointEditVC?.firstFrameImage = image
  40. }
  41. // BFLog(1, message: "aaa: \(Date().timeIntervalSince(startTime)), \(String(describing: image?.size))")
  42. }
  43. }
  44. }
  45. }
  46. }
  47. // 选中的音乐数据
  48. var selectedMusicData: PQVoiceModel?
  49. /// 缓存数据
  50. /// ["tagId":
  51. /// [
  52. /// "tagInfo":([PQStuckPointMusicTagsModel], ([UICollectionViewLayoutAttributes], CGFloat)),
  53. /// "pageList":[PQVoiceModel],
  54. /// "currentTag": PQStuckPointMusicTagsModel
  55. /// ]
  56. /// ]
  57. var cacheMusicData: [String: [String: Any]] = Dictionary<String, [String: Any]>.init()
  58. // 热搜数据
  59. var hotList: [Any] = Array<Any>.init()
  60. // 左边距离
  61. let leftMargin: CGFloat = 16
  62. // 搜索拦高度
  63. let searchTFH: CGFloat = 37
  64. // 当前页码
  65. var pageNum: Int = 0
  66. // 当前标签信息-如果是热门则为空
  67. var tagData: PQStuckPointMusicTagsModel?
  68. // 当前播放的音乐
  69. var currentPlayData: PQVoiceModel?
  70. // 当前播放的视频
  71. var playerItem: AVPlayerItem?
  72. public var reCreateVideoData: PQReCreateModel? // 再创作数据
  73. lazy var avPlayer: AVPlayer = {
  74. let avPlayer = AVPlayer()
  75. PQNotification.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avPlayer.currentItem, queue: .main) { [weak self] notify in
  76. BFLog(message: "AVPlayerItemDidPlayToEndTime = \(notify)")
  77. avPlayer.seek(to: CMTime(value: CMTimeValue((self?.currentPlayData?.startTime ?? 0) * 1000), timescale: CMTimeScale(playerTimescale)))
  78. // avPlayer.play()
  79. self?.playStuckPointMusic(itemData: nil)
  80. }
  81. PQNotification.addObserver(forName: .AVPlayerItemNewErrorLogEntry, object: avPlayer.currentItem, queue: .main) { notify in
  82. BFLog(message: "AVPlayerItemNewErrorLogEntry = \(notify)")
  83. }
  84. PQNotification.addObserver(forName: .AVPlayerItemFailedToPlayToEndTime, object: avPlayer.currentItem, queue: .main) { notify in
  85. BFLog(message: "AVPlayerItemFailedToPlayToEndTime = \(notify)")
  86. }
  87. PQNotification.addObserver(forName: .AVPlayerItemPlaybackStalled, object: avPlayer.currentItem, queue: .main) { notify in
  88. BFLog(message: "AVPlayerItemPlaybackStalled = \(notify)")
  89. }
  90. avPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: CMTimeScale(playerTimescale)), queue: .main) { [weak self] cmTime in
  91. BFLog(message: "addPeriodicTimeObserver = \(cmTime)")
  92. }
  93. return avPlayer
  94. }()
  95. // 输入框清空按钮
  96. lazy var clearBtn: UIButton = {
  97. let clearBtn = UIButton(type: .custom)
  98. clearBtn.setImage(UIImage().BF_Image(named: "icon_search_delete"), for: .normal)
  99. clearBtn.frame = CGRect(x: 0, y: 0, width: 28, height: 32)
  100. clearBtn.tag = 1
  101. clearBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  102. clearBtn.isHidden = true
  103. return clearBtn
  104. }()
  105. // 搜索框
  106. lazy var searchTF: UITextField = {
  107. let searchTF = UITextField(frame: CGRect(x: leftMargin, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth - leftMargin * 2, height: searchTFH))
  108. searchTF.font = UIFont.systemFont(ofSize: 17)
  109. searchTF.backgroundColor = PQBFConfig.shared.otherTintColor
  110. searchTF.attributedPlaceholder = NSAttributedString(string: "搜索歌曲名称/歌手", attributes: [.foregroundColor: UIColor.hexColor(hexadecimal: "#BDBDBD"), .font: UIFont.systemFont(ofSize: 14)])
  111. searchTF.textColor = PQBFConfig.shared.styleTitleColor
  112. searchTF.addCorner(corner: searchTFH / 2)
  113. searchTF.leftViewMode = .always
  114. let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 35, height: 32))
  115. let imageView = UIImageView(image: UIImage().BF_Image(named: "icon_search_s"))
  116. imageView.frame = CGRect(x: 15, y: 8, width: 16, height: 16)
  117. leftView.addSubview(imageView)
  118. searchTF.leftView = leftView
  119. searchTF.delegate = self
  120. searchTF.rightViewMode = .always
  121. let rightView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 32))
  122. rightView.addSubview(clearBtn)
  123. searchTF.rightView = rightView
  124. searchTF.delegate = self
  125. searchTF.returnKeyType = .search
  126. searchTF.addTarget(self, action: #selector(editingChanged), for: .editingChanged)
  127. return searchTF
  128. }()
  129. /// 一级分类视图
  130. lazy var topCategoryController: PQStuckPointMusicContentController = {
  131. let topCategoryController = PQStuckPointMusicContentController()
  132. addChild(topCategoryController)
  133. view.insertSubview(topCategoryController.view, belowSubview: searchController.view)
  134. topCategoryController.updateViewFrame(newFrame: CGRect(x: 0, y: searchTF.frame.maxY + cDefaultMargin * 2, width: cDefaultMargin * 12, height: view.frame.height - (searchTF.frame.maxY + cDefaultMargin * 2)))
  135. // 一级分类点击
  136. topCategoryController.didSelectedHandle = { [weak self] _, _, _, itemData in
  137. if itemData is PQStuckPointMusicTagsModel {
  138. self?.musicPageController.updateCurrentPlayMusic(isPlaying: false, isClearCurrentMusic: true)
  139. self?.searchController.updateCurrentPlayMusic(isPlaying: false, isClearCurrentMusic: true)
  140. self?.tagData = itemData as? PQStuckPointMusicTagsModel
  141. self?.dealWithCategorySelectedData(itemData: itemData as? PQStuckPointMusicTagsModel)
  142. // 更新当前标签
  143. self?.updateMusicCacheData(tagId: (itemData as? PQStuckPointMusicTagsModel)?.tagId ?? 0, tagInfo: nil, pageList: nil, currentTag: self?.tagData)
  144. }
  145. self?.playStuckPointMusic(itemData: nil, isClearCurrentMusic: true)
  146. }
  147. return topCategoryController
  148. }()
  149. /// 音乐列表视图
  150. lazy var musicPageController: PQStuckPointMusicContentController = {
  151. let musicPageController = PQStuckPointMusicContentController()
  152. musicPageController.cellHight = cDefaultMargin * 8
  153. addChild(musicPageController)
  154. view.insertSubview(musicPageController.view, belowSubview: searchController.view)
  155. musicPageController.updateViewFrame(newFrame: CGRect(x: topCategoryController.view.frame.maxX, y: topCategoryController.view.frame.minY, width: view.frame.width - topCategoryController.view.frame.maxX, height: topCategoryController.view.frame.height))
  156. musicPageController.contentType = .page
  157. musicPageController.didSelectedHandle = { [weak self] isTagsClick, _, _, itemData in
  158. if isTagsClick { // 二级分类点击
  159. self?.tagData = (itemData as? PQStuckPointMusicTagsModel)
  160. let tagId: Int64 = (itemData as? PQStuckPointMusicTagsModel)?.tagId ?? 0
  161. let parentTagId: Int64 = (itemData as? PQStuckPointMusicTagsModel)?.parentTagId ?? 0
  162. // 请求列表数据
  163. self?.requestPageListData(tagId: tagId, parentTagId: parentTagId)
  164. } else {
  165. self?.playStuckPointMusic(itemData: itemData as? PQVoiceModel)
  166. }
  167. }
  168. musicPageController.refreshHandle = { [weak self] isRefresh, _ in
  169. let tagId: Int64 = self?.tagData?.tagId ?? 0
  170. let parentTagId: Int64 = self?.tagData?.parentTagId ?? 0
  171. // self?.musicPageController.tagsInfo = (tags, tagAttributes) as? ([PQStuckPointMusicTagsModel], ([UICollectionViewLayoutAttributes], CGFloat))
  172. // 请求列表数据
  173. self?.requestPageListData(isRefresh: isRefresh, tagId: tagId, parentTagId: parentTagId)
  174. // 请求二级标签数据
  175. // self?.loadRequestSubTagsList()
  176. self?.playStuckPointMusic(itemData: nil, isClearCurrentMusic: true)
  177. }
  178. musicPageController.btnClickHandle = { [weak self] _, bgmData in
  179. // 使用音乐
  180. self?.userstuckPointMusic(musicData: bgmData as? PQVoiceModel)
  181. }
  182. return musicPageController
  183. }()
  184. /// 搜索控制器
  185. lazy var searchController: PQStuckPointMusicSearchController = {
  186. let searchController = PQStuckPointMusicSearchController()
  187. searchController.selectedDataCount = selectedDataCount
  188. searchController.selectedImageDataCount = selectedImageDataCount
  189. searchController.selectedTotalDuration = selectedTotalDuration
  190. searchController.cellHight = cDefaultMargin * 8
  191. addChild(searchController)
  192. view.addSubview(searchController.view)
  193. searchController.updateViewFrame(newFrame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei + cDefaultMargin * 2, width: view.frame.width, height: view.frame.height - (cDevice_iPhoneNavBarAndStatusBarHei + cDefaultMargin * 2)))
  194. searchController.didSelectedHandle = { [weak self] isTagsClick, _, _, itemData in
  195. if !isTagsClick {
  196. self?.view.endEditing(true)
  197. if !(itemData is PQEmptyModel) {
  198. self?.playStuckPointMusic(itemData: itemData as? PQVoiceModel)
  199. }
  200. }
  201. }
  202. searchController.btnClickHandle = { [weak self] _, bgmData in
  203. // 使用音乐
  204. self?.userstuckPointMusic(musicData: bgmData as? PQVoiceModel)
  205. }
  206. searchController.scroDidScroHandle = { [weak self] in
  207. self?.view.endEditing(true)
  208. }
  209. searchController.view.isHidden = true
  210. searchController.contentType = .serach
  211. return searchController
  212. }()
  213. lazy var emptyRemindView: PQEmptyRemindView = {
  214. let emptyRemindView = PQEmptyRemindView(frame: CGRect(x: 0, y: searchTF.frame.maxY + cDefaultMargin * 2, width: view.frame.width, height: view.frame.height - (searchTF.frame.maxY + cDefaultMargin * 2)))
  215. emptyRemindView.remindLab.font = UIFont.systemFont(ofSize: 20)
  216. emptyRemindView.remindLab.textColor = UIColor.hexColor(hexadecimal: "#575757")
  217. emptyRemindView.isHidden = true
  218. let emptyData = PQEmptyModel()
  219. emptyData.title = "暂无音乐"
  220. emptyRemindView.emptyData = emptyData
  221. emptyRemindView.fullRefreshBloc = { [weak self] _, _ in
  222. /// 请求标签数据
  223. self?.loadRequestTagsList()
  224. }
  225. view.addSubview(emptyRemindView)
  226. return emptyRemindView
  227. }()
  228. /// 已选择音乐view
  229. lazy var selectedMusciView: PQStuckPointMusicContentCell = {
  230. let selectedMusciView = PQStuckPointMusicContentCell(frame: CGRect(x: 0, y: view.frame.height - cDefaultMargin * 6, width: cScreenWidth, height: cDefaultMargin * 6))
  231. selectedMusciView.confirmContentView.backgroundColor = UIColor.hexColor(hexadecimal: "#333333")
  232. selectedMusciView.playImageView.isHidden = true
  233. selectedMusciView.confirmBtn.setTitle(" 继续使用 ", for: .normal)
  234. selectedMusciView.backgroundColor = UIColor.hexColor(hexadecimal: "#333333")
  235. view.addSubview(selectedMusciView)
  236. return selectedMusciView
  237. }()
  238. override func viewDidLoad() {
  239. super.viewDidLoad()
  240. // 拦截侧滑返回
  241. disablePopGesture().popGestureHandle = { [weak self] in
  242. self?.backBtnClick()
  243. }
  244. leftButton(image: "icon_detail_back", tintColor: PQBFConfig.shared.styleTitleColor)
  245. setTitle(title: "选择音乐主题", color: PQBFConfig.shared.styleTitleColor)
  246. view.addSubview(searchTF)
  247. /// 请求标签数据
  248. loadRequestTagsList()
  249. PQNotification.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
  250. if selectedMusicData != nil {
  251. let editerVC: PQStuckPointEditerController = PQStuckPointEditerController()
  252. editerVC.stuckPointMusicData = selectedMusicData
  253. editerVC.selectedDataCount = selectedDataCount
  254. editerVC.selectedImageDataCount = selectedImageDataCount
  255. //mdf by ak 进入编辑界面的时候去掉图片的时长
  256. editerVC.selectedTotalDuration = selectedTotalDuration - (Double(selectedImageDataCount) * 1.5)
  257. editerVC.selectedPhotoData = selectedPhotoData
  258. editerVC.reCreateVideoData = reCreateVideoData
  259. editerVC.isReCreate = true
  260. navigationController?.pushViewController(editerVC, animated: true)
  261. stuckPointEditVC = editerVC
  262. if firstFrameImage != nil {
  263. editerVC.firstFrameImage = firstFrameImage
  264. }
  265. }
  266. // 卡点视频音乐选择曝光上报
  267. PQEventTrackViewModel.baseReportUpload(businessType: .bt_windowView, objectType: .ot_view_selectSyncedUpMusic, pageSource: .sp_stuck_selectSynceedUpMusic, extParams: nil, remindmsg: "卡点视频数据上报-(曝光上报:卡点视频音乐选择页)")
  268. }
  269. override func viewWillAppear(_ animated: Bool) {
  270. // mdf by ak 修复其它界面的键盘会影响这个界面问题
  271. addKeyboardObserver()
  272. }
  273. override func viewWillDisappear(_ animated: Bool) {
  274. super.viewWillDisappear(animated)
  275. playStuckPointMusic(itemData: nil)
  276. PQNotification.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
  277. PQNotification.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
  278. }
  279. /// 按钮点击事件
  280. /// - Parameter sender: <#sender description#>
  281. /// - Returns: <#description#>
  282. @objc func btnClick(sender: UIButton) {
  283. switch sender.tag {
  284. case 1: // 清除
  285. searchTF.text = nil
  286. // emptyRemindView.isHidden = true
  287. clearBtn.isHidden = true
  288. default:
  289. break
  290. }
  291. }
  292. /// 请求标签数据
  293. /// - Returns: <#description#>
  294. func loadRequestTagsList() {
  295. PQLoadingHUB.shared.showHUB()
  296. PQStuckPointViewModel.stuckPointMusicCategoryList { [weak self] tags, _, _ in
  297. PQLoadingHUB.shared.dismissHUB()
  298. if tags.count > 0 {
  299. self?.tagData = tags.first
  300. }
  301. self?.emptyRemindView.isHidden = tags.count > 0
  302. if tags.count > 0 {
  303. self?.topCategoryController.configMusicListData(isRefresh: true, musicListData: tags)
  304. // 请求列表数据
  305. self?.requestPageListData(isHotPage: true, tagId: tags.first?.tagId ?? 0, parentTagId: 0)
  306. }
  307. }
  308. }
  309. /// 请求二级标签数据
  310. /// - Returns: <#description#>
  311. func loadRequestSubTagsList() {
  312. PQLoadingHUB.shared.showHUB()
  313. PQStuckPointViewModel.stuckPointMusicCategoryList(parentTagId: tagData?.tagId ?? 0) { [weak self] tags, _, tagAttributes in
  314. PQLoadingHUB.shared.dismissHUB()
  315. if tagAttributes != nil {
  316. let tagId: Int64 = tags.count > 0 ? (tags.first?.tagId ?? 0) : (self?.tagData?.tagId ?? 0)
  317. let parentTagId: Int64 = tags.count > 0 ? (self?.tagData?.tagId ?? 0) : 0
  318. self?.musicPageController.tagsInfo = (tags, tagAttributes) as? ([PQStuckPointMusicTagsModel], ([UICollectionViewLayoutAttributes], CGFloat))
  319. if tags.count > 0 {
  320. self?.tagData = tags.first
  321. }
  322. // 添加缓存
  323. self?.updateMusicCacheData(tagId: parentTagId > 0 ? parentTagId : tagId, isClearOld: false, tagInfo: self?.musicPageController.tagsInfo, pageList: nil, currentTag: self?.tagData)
  324. // 请求列表数据
  325. self?.requestPageListData(tagId: tagId, parentTagId: parentTagId)
  326. } else {
  327. // 重置数据
  328. self?.resetPageData(isResetData: false)
  329. }
  330. }
  331. }
  332. /// 处理cell点击回调
  333. /// - Parameter itemData: <#itemData description#>
  334. /// - Returns: <#description#>
  335. func dealWithCategorySelectedData(itemData: PQStuckPointMusicTagsModel?) {
  336. tagData = itemData
  337. // 重置数据
  338. resetPageData(isResetData: true)
  339. let cacheData = getMusicCacheData(itemData: itemData)
  340. if cacheData != nil, (cacheData?.0 != nil && (cacheData?.0?.0.count ?? 0) > 0) || (cacheData?.1 != nil && (cacheData?.1?.count ?? 0) > 0) {
  341. musicPageController.tagsInfo = cacheData?.0
  342. musicPageController.configMusicListData(isRefresh: true, musicListData: cacheData?.1 ?? [])
  343. pageNum = ((cacheData?.1?.count ?? 0) / 20) + 1
  344. tagData = cacheData?.2
  345. } else {
  346. pageNum = 1
  347. // 请求二级标签数据
  348. loadRequestSubTagsList()
  349. }
  350. }
  351. /// 请求列表数据
  352. /// - Parameters:
  353. /// - tagId: <#tagId description#>
  354. /// - parentTagId: <#parentTagId description#>
  355. /// - tagsInfo: <#tagsInfo description#>
  356. /// - Returns: <#description#>
  357. func requestPageListData(isRefresh: Bool = true, isHotPage: Bool = false, tagId: Int64, parentTagId: Int64) {
  358. PQLoadingHUB.shared.showHUB()
  359. if isRefresh {
  360. pageNum = 1
  361. }
  362. PQStuckPointViewModel.stuckPointMusicPageList(tagId: tagId, parentTagId: parentTagId, pageNum: pageNum, videoCount: selectedDataCount - selectedImageDataCount, imageCount: selectedImageDataCount, totalDuration: selectedTotalDuration) { [weak self] musicInfo, _ in
  363. PQLoadingHUB.shared.dismissHUB()
  364. if musicInfo.count > 0 {
  365. self?.pageNum = (self?.pageNum ?? 0) + 1
  366. }
  367. self?.musicPageController.configMusicListData(isRefresh: isRefresh, musicListData: musicInfo)
  368. // 添加缓存
  369. self?.updateMusicCacheData(tagId: parentTagId > 0 ? parentTagId : tagId, isClearOld: isRefresh, tagInfo: nil, pageList: musicInfo, currentTag: nil)
  370. if musicInfo.count > 0, isHotPage, isRefresh {
  371. self?.hotList = musicInfo
  372. self?.searchController.hotList = self?.hotList ?? []
  373. }
  374. }
  375. }
  376. /// 重置页面数据
  377. /// - Parameter isResetData: <#isResetData description#>
  378. /// - Returns: <#description#>
  379. func resetPageData(isResetData: Bool) {
  380. musicPageController.tagsInfo = nil
  381. musicPageController.configMusicListData(isResetData: isResetData, isRefresh: true, musicListData: [])
  382. pageNum = 1
  383. }
  384. var avPlayerTimeObserver:Any?
  385. /// 播放音乐
  386. /// - Parameter itemData: <#itemData description#>
  387. func playStuckPointMusic(itemData: PQVoiceModel?, isClearCurrentMusic: Bool = false) {
  388. if itemData != nil, currentPlayData != itemData {
  389. if !isValidURL(url: itemData?.musicPath ?? "") {
  390. cShowHUB(superView: nil, msg: "本歌曲暂无伴奏版本哦~")
  391. return
  392. }
  393. avPlayer.pause()
  394. playerItem?.removeObserver(self, forKeyPath: "status")
  395. playerItem?.removeObserver(self, forKeyPath: "error")
  396. if avPlayerTimeObserver != nil {
  397. avPlayer.removeTimeObserver(avPlayerTimeObserver as Any)
  398. }
  399. playerItem = AVPlayerItem(url: URL(string: itemData?.musicPath ?? "")!)
  400. if (itemData?.endTime ?? 0) > 0, (itemData?.endTime ?? 0) > (itemData?.startTime ?? 0) {
  401. playerItem?.forwardPlaybackEndTime = CMTime(value: CMTimeValue((itemData?.endTime ?? 0) * playerTimescale), timescale: CMTimeScale(playerTimescale))
  402. }
  403. avPlayer.replaceCurrentItem(with: playerItem)
  404. playerItem?.addObserver(self, forKeyPath: "status", options: .new, context: nil)
  405. playerItem?.addObserver(self, forKeyPath: "error", options: .new, context: nil)
  406. avPlayer.seek(to: CMTime(value: CMTimeValue((itemData?.startTime ?? 0) * playerTimescale), timescale: CMTimeScale(playerTimescale)))
  407. avPlayer.play()
  408. avPlayerTimeObserver = avPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 10), queue: DispatchQueue.global()) {[weak self] time in
  409. if fabs(CMTimeGetSeconds(time) - (itemData?.startTime ?? 0)) > 0.1 {
  410. self?.avPlayer.removeTimeObserver(self?.avPlayerTimeObserver as Any)
  411. self?.avPlayerTimeObserver = nil
  412. // 停止cell loading动画
  413. PQNotification.post(name: NSNotification.Name(rawValue: "MusicContentCellIconLoadingAnimationStop"), object: nil)
  414. }
  415. // 进度监控
  416. // BFLog(1, message: "\(Int(CMTimeGetSeconds((self?.playerItem?.asset.duration)!))),curr:\(CMTimeGetSeconds(time))")
  417. }
  418. // let player = TXVodPlayer()
  419. // let config = TXVodPlayConfig()
  420. // config.cacheFolderPath = videoCacheDirectory
  421. // config.maxCacheItems = 0
  422. // player.config = config
  423. //// player.vodDelegate = self
  424. // player.setRenderMode(.RENDER_MODE_FILL_EDGE)
  425. // player.startPlay("https://clipres.yishihui.com/longvideo/material/voice/prod/20210512/MUSIC_QQ_000T1Ws32MWrUj")
  426. currentPlayData = itemData
  427. } else if itemData != nil, avPlayer.rate == 0.0 {
  428. avPlayer.play()
  429. } else {
  430. avPlayer.pause()
  431. musicPageController.updateCurrentPlayMusic(isPlaying: false, isClearCurrentMusic: isClearCurrentMusic)
  432. searchController.updateCurrentPlayMusic(isPlaying: false, isClearCurrentMusic: isClearCurrentMusic)
  433. }
  434. if isClearCurrentMusic {
  435. avPlayer.pause()
  436. currentPlayData = nil
  437. }
  438. }
  439. /// 使用卡点音乐
  440. /// - Parameter musicData: <#musicData description#>
  441. /// - Returns: <#description#>
  442. func userstuckPointMusic(musicData: PQVoiceModel?) {
  443. if musicData != nil {
  444. let editerVC: PQStuckPointEditerController = PQStuckPointEditerController()
  445. editerVC.selectedDataCount = selectedDataCount
  446. editerVC.selectedImageDataCount = selectedImageDataCount
  447. editerVC.selectedTotalDuration = selectedTotalDuration - (Double(selectedImageDataCount) * 1.5)
  448. editerVC.stuckPointMusicData = musicData
  449. editerVC.selectedPhotoData = selectedPhotoData
  450. navigationController?.pushViewController(editerVC, animated: true)
  451. stuckPointEditVC = editerVC
  452. if firstFrameImage != nil {
  453. editerVC.firstFrameImage = firstFrameImage
  454. }
  455. }
  456. }
  457. /// 返回按钮操作
  458. override func backBtnClick() {
  459. if searchController.view.isHidden {
  460. navigationController?.popViewController(animated: true)
  461. // 点击上报:选择音乐分类
  462. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_back, pageSource: .sp_stuck_selectSynceedUpMusic, extParams: nil, remindmsg: "卡点视频数据上报-(点击上报:返回按钮)")
  463. } else {
  464. hiddenSearchController()
  465. // 点击上报:返回按钮
  466. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_back, pageSource: .sp_stuck_searchSyncedUpMusic, extParams: nil, remindmsg: "卡点视频数据上报-(点击上报:返回按钮)")
  467. }
  468. }
  469. /// 进入后台暂停播放
  470. @objc func enterBackground() {
  471. playStuckPointMusic(itemData: nil)
  472. }
  473. deinit {
  474. PQNotification.removeObserver(self)
  475. PQNotification.removeObserver(self.avPlayer.currentItem as Any)
  476. avPlayer.currentItem?.removeObserver(self, forKeyPath: "status")
  477. avPlayer.currentItem?.removeObserver(self, forKeyPath: "error")
  478. avPlayer.pause()
  479. avPlayer.replaceCurrentItem(with: nil)
  480. playerItem = nil
  481. }
  482. }
  483. // MARK: - 音乐搜索相关
  484. /// 音乐搜索相关
  485. extension PQStuckPointMusicController: UITextFieldDelegate {
  486. /// 点击输入框
  487. @objc func editingChanged() {
  488. if searchTF.text != nil, (searchTF.text?.count ?? 0) > 0, searchTF.markedTextRange == nil {
  489. if searchTF.text?.isSpace ?? false {
  490. return
  491. }
  492. // loadSearchData()
  493. clearBtn.isHidden = false
  494. }
  495. }
  496. /// 搜索
  497. @objc func loadSearchData() {
  498. if searchTF.text != nil, (searchTF.text?.count ?? 0) > 0, searchTF.text?.isSpace ?? false {
  499. cShowHUB(superView: nil, msg: "搜索内容不能为空")
  500. return
  501. }
  502. if searchTF.text != nil, (searchTF.text?.count ?? 0) > 0 {
  503. BFLog(message: "背景音乐--开始搜索背景音乐-1")
  504. searchController.loadSearchData(keyword: searchTF.text)
  505. }
  506. }
  507. /// 添加键盘监听
  508. /// - Returns: <#description#>
  509. func addKeyboardObserver() {
  510. // 监听键盘的显示和隐藏
  511. PQNotification.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
  512. PQNotification.addObserver(self, selector: #selector(keyboardWillHidden), name: UIResponder.keyboardWillHideNotification, object: nil)
  513. }
  514. /// 键盘显示
  515. /// - Parameter notification: <#notification description#>
  516. @objc func keyboardWillShow(notification: Notification) {
  517. let duration: TimeInterval = TimeInterval("\(notification.userInfo?["UIKeyboardAnimationDurationUserInfoKey"] ?? "1")") ?? 1
  518. UIView.animate(withDuration: duration) { [weak self] in
  519. self?.navTitleLabel?.alpha = 0
  520. self?.searchTF.frame = CGRect(x: cDefaultMargin * 5, y: (cDevice_iPhoneNavBarHei - (self?.searchTFH ?? 37.0)) / 2 + cDevice_iPhoneStatusBarHei, width: cScreenWidth - cDefaultMargin * 7, height: self?.searchTFH ?? 37.0)
  521. } completion: { _ in
  522. }
  523. searchController.view.isHidden = false
  524. hotList.forEach { item in
  525. if item is PQVoiceModel {
  526. (item as? PQVoiceModel)?.isSelected = false
  527. (item as? PQVoiceModel)?.isPlaying = false
  528. }
  529. }
  530. musicPageController.collectionView.reloadData()
  531. searchController.hotList = hotList
  532. playStuckPointMusic(itemData: nil)
  533. // 曝光上报:音乐素材搜索页
  534. PQEventTrackViewModel.baseReportUpload(businessType: .bt_windowView, objectType: .ot_view_searchSyncedUpMusic, pageSource: .sp_stuck_searchSyncedUpMusic, extParams: nil, remindmsg: "卡点视频数据上报-(曝光上报:音乐素材搜索页)")
  535. }
  536. /// 键盘将要隐藏
  537. @objc func keyboardWillHidden() {}
  538. /// 隐藏搜索界面
  539. /// - Returns: <#description#>
  540. func hiddenSearchController() {
  541. view.endEditing(true)
  542. UIView.animate(withDuration: 0.3) { [weak self] in
  543. self?.navTitleLabel?.alpha = 1
  544. self?.searchTF.frame = CGRect(x: self?.leftMargin ?? 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth - (self?.leftMargin ?? 0) * 2, height: self?.searchTFH ?? 0)
  545. } completion: { _ in
  546. }
  547. clearBtn.isHidden = true
  548. searchController.view.isHidden = true
  549. // searchTF.text = nil
  550. // searchController.clearData()
  551. // currentPlayData?.isSelected = false
  552. // currentPlayData?.isPlaying = false
  553. playStuckPointMusic(itemData: nil)
  554. }
  555. override func touchesBegan(_: Set<UITouch>, with _: UIEvent?) {
  556. view.endEditing(true)
  557. }
  558. func textFieldShouldReturn(_ textField: UITextField) -> Bool {
  559. loadSearchData()
  560. view.endEditing(true)
  561. if textField.text == nil || (textField.text?.count ?? 0) <= 0 {
  562. cShowHUB(superView: nil, msg: "请先输入搜索内容")
  563. }
  564. return true
  565. }
  566. }
  567. // MARK: - 缓存相关
  568. //缓存歌曲列表 KEY
  569. let kSaveMusicListDatas = "kSaveMusicListDatas"
  570. /// 缓存相关
  571. extension PQStuckPointMusicController {
  572. /// 获取缓存数据
  573. /// - Parameter itemData: <#itemData description#>
  574. /// - Returns: <#description#>
  575. func getMusicCacheData(itemData: PQStuckPointMusicTagsModel?) -> (([PQStuckPointMusicTagsModel], ([UICollectionViewLayoutAttributes], CGFloat))?, [PQVoiceModel]?, PQStuckPointMusicTagsModel?)? {
  576. let saveMusicListStr = getUserDefaults(key: kSaveMusicListDatas)
  577. if(((saveMusicListStr as? String)?.count ?? 0 ) > 0){
  578. let cacheMusicDataTemp = jsonStringToDictionary(saveMusicListStr as! String)
  579. if itemData != nil, cacheMusicData.keys.contains("\(itemData?.tagId ?? 0)") {
  580. let tempData = cacheMusicDataTemp?["\(itemData?.tagId ?? 0)"] as? Dictionary<String, Any>
  581. return (tempData?["tagInfo"] as? ([PQStuckPointMusicTagsModel],([UICollectionViewLayoutAttributes], CGFloat)),tempData?["pageList"] as? [PQVoiceModel],tempData?["currentTag"] as? PQStuckPointMusicTagsModel)
  582. } else {
  583. return nil
  584. }
  585. }else{ return nil}
  586. }
  587. /// 更新缓存
  588. /// - Parameters:
  589. /// - tagId: 一级分类id
  590. /// - isClearOld: 是否清空老数据
  591. /// - tagInfo: 二级标签信息
  592. /// - pageList: 音乐数据
  593. /// - currentTag: 当前页当前标签
  594. /// - Returns: <#description#>
  595. func updateMusicCacheData(tagId: Int64, isClearOld: Bool = false, tagInfo: ([PQStuckPointMusicTagsModel], ([UICollectionViewLayoutAttributes], CGFloat))?, pageList: [PQVoiceModel]?, currentTag: PQStuckPointMusicTagsModel?) {
  596. if (tagInfo != nil && (tagInfo?.0.count ?? 0) > 0) || (pageList != nil && (pageList?.count ?? 0) > 0) || currentTag != nil {
  597. if cacheMusicData.keys.contains("\(tagId)") {
  598. var tempDic = cacheMusicData["\(tagId)"]
  599. if tagInfo != nil, (tagInfo?.0.count ?? 0) > 0 {
  600. tempDic?["tagInfo"] = tagInfo!
  601. }
  602. if pageList != nil, (pageList?.count ?? 0) > 0 {
  603. var oldPageList: [PQVoiceModel] = (tempDic?["pageList"] as? [PQVoiceModel]) ?? Array<PQVoiceModel>.init()
  604. if isClearOld {
  605. oldPageList.removeAll()
  606. }
  607. if oldPageList.count > 0 {
  608. tempDic?["pageList"] = oldPageList + pageList!
  609. } else {
  610. tempDic?["pageList"] = pageList!
  611. }
  612. }
  613. if currentTag != nil {
  614. tempDic?["currentTag"] = currentTag!
  615. }
  616. cacheMusicData["\(tagId)"] = tempDic ?? Dictionary<String, Any>.init()
  617. } else {
  618. var tempDic: [String: Any] = Dictionary<String, Any>.init()
  619. if tagInfo != nil, (tagInfo?.0.count ?? 0) > 0 {
  620. tempDic["tagInfo"] = tagInfo!
  621. }
  622. if pageList != nil, (pageList?.count ?? 0) > 0 {
  623. tempDic["pageList"] = pageList!
  624. }
  625. if currentTag != nil {
  626. tempDic["currentTag"] = currentTag!
  627. }
  628. cacheMusicData["\(tagId)"] = tempDic
  629. }
  630. saveUserDefaults(key: kSaveMusicListDatas, value: dictionaryToJsonString(cacheMusicData) as Any)
  631. }
  632. }
  633. }
  634. extension PQStuckPointMusicController {
  635. override func observeValue(forKeyPath keyPath: String?, of object: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
  636. if object is AVPlayerItem, keyPath == "status" {
  637. BFLog(message: "AVPlayerItem - status = \((object as! AVPlayerItem).status.rawValue)")
  638. switch (object as! AVPlayerItem).status {
  639. case .unknown:
  640. break
  641. case .readyToPlay:
  642. break
  643. case .failed:
  644. break
  645. default:
  646. break
  647. }
  648. } else if object is AVPlayerItem, keyPath == "error" {
  649. BFLog(message: "AVPlayerItem - error = \(String(describing: (object as! AVPlayerItem).error))")
  650. }
  651. }
  652. }