PQStuckPointMusicController.swift 33 KB

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