detail.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. // pages/video/detail.js
  2. import {getVideoDetail, getRealVideoUrl, postWithCommonParams, getRelatedVideos, getUuid} from '../../api/api'
  3. import {rnd, SendEvent} from '../../utils/util'
  4. import {apple} from "../../config/config";
  5. const app = getApp();
  6. let interstitialAd = null
  7. let insertAdInterval = null
  8. Page({
  9. data: {
  10. videoId: '',
  11. videoInfo: null,
  12. videoDetail: null,
  13. episodeList: [],
  14. currentEpisodeIndex: 0,
  15. currentEpisode: null, // 当前播放的剧集
  16. isPlaying: false,
  17. isLoading: true,
  18. title: '',
  19. scrollHeight: 0,
  20. windowHeight: 0,
  21. realVideoUrl: '', // 存储真实的视频播放地址
  22. videoStatus: 0, // 0 未播放 1 播放中 2 暂停
  23. duration: 0, // 视频总时长
  24. currentTime: 0, // 当前播放时间
  25. relatedVideos: [],
  26. tagId: 0,
  27. share_count: 1,
  28. scene: 0, // 场景值
  29. },
  30. onLoad: function (options) {
  31. const videoId = parseInt(options.id);
  32. const videoTitle = options.title ? decodeURIComponent(options.title) : '';
  33. const tagId = parseInt(options.tag_id);
  34. this.setData({
  35. scene: wx.getStorageSync('scene') || 1001
  36. })
  37. SendEvent('show_video', {
  38. "video_id": videoId,
  39. "tag_id": tagId,
  40. "scene": this.data.scene,
  41. "uuid": app.globalData.uuid
  42. });
  43. console.log('加载视频详情页,ID:', videoId, '标题:', videoTitle);
  44. // 获取系统信息
  45. this.getSystemInfo();
  46. // 如果有传递标题,则直接使用
  47. if (videoTitle) {
  48. this.setData({
  49. title: videoTitle,
  50. videoId: videoId,
  51. isLoading: true,
  52. tagId: tagId
  53. });
  54. } else {
  55. this.setData({
  56. videoId: videoId,
  57. isLoading: true,
  58. tagId: tagId
  59. });
  60. }
  61. this.rewardVideoLoad();
  62. this.interstitalLoad();
  63. this.loadVideoDetail(videoId);
  64. app.globalData.isEnterVideoDetail = true;
  65. },
  66. onShow() {
  67. const pages = getCurrentPages();
  68. const currentPage = pages[pages.length - 1];
  69. const options = currentPage.options;
  70. console.log('scene', this.data.scene)
  71. if (+options.share_count === 1 && this.data.share_count === 1) {
  72. this.setData({
  73. share_count: 2
  74. })
  75. if ((this.data.scene === 1007 || this.data.scene === 1008)) {
  76. console.log('准备跳转novel')
  77. this.goToBookDetail()
  78. } else {
  79. console.log('没有跳转novel')
  80. }
  81. } else if (+options.share_count === 1 && this.data.share_count === 2) {
  82. this.setData({
  83. share_count: 3
  84. })
  85. }
  86. },
  87. /**
  88. * 获取uuid
  89. */
  90. getUUID() {
  91. return new Promise((resolve, reject) => {
  92. wx.login({
  93. success: res => {
  94. let body = {
  95. code: res.code,
  96. source_type: 1,
  97. source_id: ''
  98. }
  99. getUuid(body).then(async res => {
  100. resolve(res.uuid)
  101. }).catch((err) => {
  102. reject(err)
  103. })
  104. }
  105. })
  106. })
  107. },
  108. // 获取系统信息
  109. getSystemInfo() {
  110. const systemInfo = wx.getSystemInfoSync();
  111. this.setData({
  112. windowHeight: systemInfo.windowHeight
  113. });
  114. },
  115. // 加载视频详情
  116. async loadVideoDetail(id) {
  117. try {
  118. wx.showLoading({
  119. title: '加载中...',
  120. mask: true
  121. });
  122. //getUuid
  123. if (!getApp().globalData.uuid) {
  124. await this.getUUID().then(uuid => {
  125. getApp().globalData.uuid = uuid
  126. })
  127. }
  128. // 构建请求参数
  129. const params = {
  130. id,
  131. tag_id: this.data.tagId,
  132. uuid: getApp().globalData.uuid
  133. };
  134. const videoDetail = await getVideoDetail(params);
  135. console.log('视频详情数据:', videoDetail);
  136. if (videoDetail) {
  137. // 处理视频信息
  138. const formattedVideoInfo = {
  139. id: videoDetail.id,
  140. title: videoDetail.title || this.data.title,
  141. cover: videoDetail.cover_image,
  142. playCount: videoDetail.views || '0',
  143. playUV: videoDetail.play_uv || '0',
  144. videoUrl: '', // 暂时为空,等待获取真实播放地址
  145. description: videoDetail.description || '暂无简介',
  146. tags: videoDetail.tag_ids || [],
  147. likeCount: videoDetail.like || '0',
  148. shareCount: videoDetail.share || '0',
  149. isCollect: videoDetail.is_collect === 1,
  150. video_type: videoDetail.video_type, // 保存原始视频类型
  151. tag_ids: videoDetail.tag_ids // 保存标签ID列表
  152. };
  153. this.setData({
  154. videoInfo: formattedVideoInfo,
  155. title: formattedVideoInfo.title,
  156. isLoading: false
  157. });
  158. // 获取真实播放地址
  159. this.getVideoPlayUrl(videoDetail);
  160. this.interstitalEnterPageShow()
  161. // 加载相关视频
  162. this.loadRelatedVideos(videoDetail.tag_ids);
  163. } else {
  164. this.setData({
  165. isLoading: false
  166. });
  167. wx.showToast({
  168. title: '获取视频详情失败',
  169. icon: 'none'
  170. });
  171. }
  172. } catch (error) {
  173. console.error('获取视频详情失败:', error);
  174. this.setData({
  175. isLoading: false
  176. });
  177. wx.showToast({
  178. title: '获取视频详情失败',
  179. icon: 'none'
  180. });
  181. } finally {
  182. wx.hideLoading();
  183. }
  184. },
  185. // 获取视频播放地址
  186. async getVideoPlayUrl(videoDetail) {
  187. console.log(`videoDetail`, videoDetail)
  188. try {
  189. // 根据视频类型进行处理
  190. switch (videoDetail.video_type) {
  191. case 3: // OAS视频
  192. // 获取真实播放地址
  193. const tagId = videoDetail.tag_ids && videoDetail.tag_ids.length > 0 ? videoDetail.tag_ids[0] : 2072;
  194. let parseRes = await getRealVideoUrl(videoDetail.id, this.data.tagId); // 此处需要使用传入的tag_id,否则视频有多个tag_id时,会无法获取到真实播放地址
  195. console.log('parseRes', parseRes)
  196. // 默认视频为免费视频
  197. videoDetail.lock = false
  198. // 标记暂停插屏是否显示过
  199. videoDetail.pauseInsertAd = false
  200. // 默认视频不显示插屏
  201. videoDetail.insertAd = false
  202. videoDetail.adType = 'video'
  203. videoDetail.likeNum = rnd(101, 209) / 10
  204. videoDetail.like = false
  205. videoDetail.shareNum = rnd(51, 99) / 10
  206. if (videoDetail.video_type !== 4) {
  207. videoDetail.playUrl = parseRes.url_info[0].url
  208. }
  209. if (videoDetail.is_top === 1) {
  210. videoDetail.top = true
  211. }
  212. this.setData({
  213. isLoading: false,
  214. videoDetail: videoDetail
  215. })
  216. break;
  217. case 4: // 微信视频
  218. // 直接使用video_vid
  219. this.setData({
  220. 'videoInfo.videoUrl': videoDetail.video_vid
  221. });
  222. break;
  223. default:
  224. // 默认使用url字段
  225. if (videoDetail.url) {
  226. this.setData({
  227. 'videoInfo.videoUrl': videoDetail.url
  228. });
  229. } else {
  230. throw new Error('不支持的视频类型');
  231. }
  232. }
  233. } catch (error) {
  234. console.error('获取视频播放地址失败:', error);
  235. wx.showToast({
  236. title: '无法获取视频播放地址',
  237. icon: 'none'
  238. });
  239. }
  240. },
  241. // 切换剧集
  242. switchEpisode(e) {
  243. const index = e.currentTarget.dataset.index;
  244. if (index === this.data.currentEpisodeIndex) return;
  245. const episode = this.data.episodeList[index];
  246. if (!episode) return;
  247. this.setData({
  248. currentEpisodeIndex: index,
  249. currentEpisode: episode,
  250. isPlaying: true
  251. });
  252. // 获取视频实例并播放
  253. const videoContext = wx.createVideoContext('myVideo');
  254. videoContext.play();
  255. },
  256. // 视频播放事件
  257. onVideoPlay() {
  258. this.setData({
  259. isPlaying: true,
  260. videoStatus: 1
  261. });
  262. // 可以添加播放埋点
  263. console.log('视频开始播放:', this.data.videoInfo.id);
  264. },
  265. // 视频暂停事件
  266. onVideoPause() {
  267. this.setData({
  268. isPlaying: false,
  269. videoStatus: 2
  270. });
  271. console.log('视频暂停:', this.data.videoInfo.id)
  272. },
  273. // 视频播放结束事件
  274. onVideoEnd() {
  275. this.setData({
  276. isPlaying: false,
  277. videoStatus: 0
  278. });
  279. // 可以添加结束埋点
  280. console.log('视频播放结束:', this.data.videoInfo.id);
  281. },
  282. // 视频时间更新事件
  283. onVideoUpdate(e) {
  284. const {currentTime, duration} = e.detail;
  285. this.setData({
  286. currentTime,
  287. duration
  288. });
  289. },
  290. // 视频元数据加载完成
  291. onVideoLoaded(e) {
  292. console.log('视频元数据加载完成', e.detail);
  293. },
  294. // 视频播放
  295. makeVideoPlay() {
  296. if (!this.data.videoInfo.lock && this.data.videoStatus == 2) {
  297. let video = this.getCurrentVideoContext()
  298. video.play()
  299. }
  300. },
  301. // 视频暂停
  302. makeVideoPause() {
  303. if (this.data.videoStatus == 1) {
  304. let video = this.getCurrentVideoContext()
  305. video.pause()
  306. }
  307. },
  308. // 获取当前视频播放器实例
  309. getCurrentVideoContext() {
  310. return wx.createVideoContext(`myVideo`, this)
  311. },
  312. // 点击视频控制区
  313. onClickVideoControls() {
  314. if (this.data.videoStatus === 1) {
  315. // 当前正在播放,暂停视频
  316. const videoContext = wx.createVideoContext('myVideo');
  317. videoContext.pause();
  318. this.insertAdType = 1;
  319. this.interstitalPlayFn();
  320. } else {
  321. // 当前已暂停,开始播放
  322. const videoContext = wx.createVideoContext('myVideo');
  323. videoContext.play();
  324. }
  325. },
  326. // 点击相关推荐视频
  327. onRelatedVideoTap(e) {
  328. console.log(e.currentTarget.dataset)
  329. const videoId = e.currentTarget.dataset.id;
  330. const video = this.data.relatedVideos.find(item => item.id === videoId);
  331. // console.log('video',video)
  332. if (!video) {
  333. wx.showToast({
  334. title: '视频信息获取失败',
  335. icon: 'none'
  336. });
  337. return;
  338. }
  339. const tag_id = video.tag_ids[0];
  340. // console.log('tag_id',tag_id)
  341. // 显示加载中提示
  342. wx.showLoading({
  343. title: '加载中...',
  344. mask: true
  345. });
  346. // 将标题进行URI编码处理
  347. const encodedTitle = encodeURIComponent(video.title || '');
  348. this.unlockUrl = `/pages/video/detail?id=${videoId}&title=${encodedTitle}&tag_id=${tag_id}`;
  349. SendEvent('click_recommend_video', {"video_id": videoId, "tag_id": tag_id, "uuid": app.globalData.uuid});
  350. if (!video.lock) {
  351. // 跳转到视频详情页
  352. wx.navigateTo({
  353. url: this.unlockUrl,
  354. success: () => {
  355. wx.hideLoading();
  356. },
  357. fail: (err) => {
  358. console.error('跳转失败:', err);
  359. wx.hideLoading();
  360. wx.showToast({
  361. title: '跳转失败',
  362. icon: 'none'
  363. });
  364. }
  365. });
  366. } else {
  367. this.rewardType = 2
  368. this.rewardVideoPlayFn()
  369. }
  370. },
  371. // 跳转到书籍详情
  372. goToBookDetail() {
  373. getApp().globalData.isShowDialog = true
  374. goToBookDetail({
  375. bookId: 10745,
  376. wxBookId: 'A1L2D5CPFHZW6Gzr9VdypmijDJ',
  377. chapterId: null
  378. });
  379. },
  380. // 返回上一页
  381. goBack() {
  382. wx.navigateBack({
  383. delta: 1
  384. }
  385. );
  386. // wx.redirectTo({
  387. // url: '/pages/video/video',
  388. // });
  389. console.log('返回上一页')
  390. SendEvent('click_return_list', {
  391. "video_id": this.data.videoId,
  392. "tag_id": this.data.tagId,
  393. "uuid": app.globalData.uuid
  394. });
  395. },
  396. // 分享按钮点击事件
  397. onShareTap(e) {
  398. // 阻止事件冒泡
  399. e.stopPropagation();
  400. },
  401. // 分享给朋友
  402. onShareAppMessage(e) {
  403. const jump = wx.getStorageSync('jump'); // 是否跳转阅读器
  404. let url = '';
  405. // 如果是从推荐视频分享
  406. if (e.target && e.target.dataset.video) {
  407. const video = e.target.dataset.video;
  408. url = `/pages/video/video?shared_video_id=${video.id}&title=${encodeURIComponent(video.title)}&tag_id=${video.tag_ids[0]}&share_count=1&type=share`;
  409. if (jump === 1) {
  410. url += '&jump=1';
  411. }
  412. SendEvent('click_share', {"video_id": video.id, "tag_id": video.tag_ids[0]});
  413. return {
  414. title: video.title,
  415. path: url,
  416. imageUrl: video.cover_image,
  417. success: function (res) {
  418. wx.showToast({
  419. title: '分享成功',
  420. icon: 'success',
  421. duration: 1500
  422. });
  423. },
  424. fail: function (res) {
  425. wx.showToast({
  426. title: '分享失败',
  427. icon: 'none',
  428. duration: 1500
  429. });
  430. }
  431. };
  432. }
  433. // 默认分享当前视频
  434. const videoInfo = this.data.videoInfo;
  435. url = `/pages/video/video?shared_video_id=${videoInfo.id}&title=${encodeURIComponent(videoInfo.title)}&tag_id=${videoInfo.tag_ids[0]}&share_count=1&type=share`;
  436. if (jump === 1) {
  437. url += '&jump=1';
  438. }
  439. SendEvent('click_share', {"video_id": videoInfo.id, "tag_id": videoInfo.tag_ids[0]});
  440. return {
  441. title: videoInfo.title,
  442. path: url,
  443. imageUrl: videoInfo.cover,
  444. success: function (res) {
  445. wx.showToast({
  446. title: '分享成功',
  447. icon: 'success',
  448. duration: 1500
  449. });
  450. },
  451. fail: function (res) {
  452. wx.showToast({
  453. title: '分享失败',
  454. icon: 'none',
  455. duration: 1500
  456. });
  457. }
  458. };
  459. },
  460. // 分享到朋友圈
  461. onShareTimeline() {
  462. const videoInfo = this.data.videoInfo;
  463. const jump = wx.getStorageSync('jump'); // 是否跳转阅读器
  464. let params = `shared_video_id=${videoInfo.id}&title=${encodeURIComponent(videoInfo.title)}&tag_id=${videoInfo.tag_ids[0]}&share_count=1&type=share`;
  465. if (jump === 1) {
  466. params += '&jump=1';
  467. }
  468. SendEvent('click_share_moment', {"video_id": videoInfo.id, "tag_id": videoInfo.tag_ids[0]});
  469. return {
  470. title: videoInfo.title,
  471. query: params,
  472. imageUrl: videoInfo.cover
  473. };
  474. },
  475. // 收藏视频
  476. toggleCollect() {
  477. const isCollect = !this.data.videoInfo.isCollect;
  478. this.setData({
  479. 'videoInfo.isCollect': isCollect
  480. });
  481. wx.showToast({
  482. title: isCollect ? '收藏成功' : '已取消收藏',
  483. icon: 'success'
  484. });
  485. },
  486. // 点赞视频
  487. likeVideo() {
  488. const currentLikes = parseInt(this.data.videoInfo.likeCount) || 0;
  489. this.setData({
  490. 'videoInfo.likeCount': (currentLikes + 1).toString()
  491. });
  492. wx.showToast({
  493. title: '点赞成功',
  494. icon: 'success'
  495. });
  496. },
  497. // 滚动事件
  498. onScroll(e) {
  499. this.setData({
  500. scrollHeight: e.detail.scrollTop
  501. });
  502. },
  503. // 加载相关视频
  504. async loadRelatedVideos(tagIds) {
  505. try {
  506. // 使用第一个标签ID获取相关视频
  507. const tagId = tagIds && tagIds.length > 0 ? tagIds[0] : 2072;
  508. console.log("原始ID:", apple.ghid)
  509. const relatedVideos = await getRelatedVideos({tag_id: tagId, gh_id: apple.ghid});
  510. // 需要播放激励视频的索引
  511. let rewardIndexList = []
  512. console.log('详情激励配置', app.globalData.ads_config)
  513. if (app.globalData.ads_config.detailListLockIndex) {
  514. rewardIndexList = app.globalData.ads_config.detailListLockIndex.split(' ').map(i => {
  515. return i - 1
  516. })
  517. }
  518. console.log('详情激励顺序', rewardIndexList)
  519. // 处理相关视频数据
  520. // const formattedVideos = relatedVideos.map((video,index) => ({
  521. // id: video.id,
  522. // title: video.title,
  523. // adType:'video',
  524. // tag_id: video.tag_ids[0],
  525. // cover_image: video.cover_image,
  526. // playCount: video.views || '0',
  527. // likeCount: video.like || '0',
  528. // shareCount: video.share || '0',
  529. // }));
  530. const formattedVideos = relatedVideos.map((video, index) => {
  531. video.adType = 'video'
  532. video.lock = rewardIndexList.includes(index);
  533. return video
  534. });
  535. this.setData({
  536. relatedVideos: formattedVideos
  537. });
  538. console.log('相关视频', formattedVideos)
  539. } catch (error) {
  540. console.error('获取相关视频失败:', error);
  541. }
  542. },
  543. // 激励视频加载
  544. rewardVideoLoad() {
  545. if (wx.createRewardedVideoAd) {
  546. let that = this
  547. this.videoAd = wx.createRewardedVideoAd({
  548. adUnitId: apple.ads.rewardAdId
  549. })
  550. this.videoAd.onLoad(() => {
  551. console.log('详情页激励load success')
  552. })
  553. this.videoAd.onError((err) => {
  554. console.log('详情页激励load error', err)
  555. })
  556. this.videoAd.onClose((res) => {
  557. // 用户点击了【关闭广告】按钮
  558. this.setData({
  559. isLoadingVideo: false
  560. });
  561. console.log('详情页激励close', res)
  562. if (res && res.isEnded) {
  563. // 发放奖励
  564. if (this.rewardType === 1) {
  565. this.setData({
  566. [`videoInfo.lock`]: false,
  567. })
  568. const video = this.getCurrentVideoContext()
  569. video.play()
  570. } else {
  571. wx.redirectTo({
  572. url: this.unlockUrl
  573. })
  574. }
  575. } else {
  576. // 未看完整视频
  577. // wx.showModal({
  578. // title: '提示',
  579. // content: '您需要观看完毕才能进行文章查看,重新播放广告吗?',
  580. // success (res) {
  581. // if (res.confirm) {
  582. // that.rewardVideoPlayFn()
  583. // } else if (res.cancel) {
  584. // console.log('用户点击取消')
  585. // }
  586. // }
  587. // })
  588. }
  589. })
  590. }
  591. },
  592. // 激励视频展示
  593. rewardVideoPlayFn() {
  594. wx.showLoading({
  595. title: '解锁视频中',
  596. })
  597. setTimeout(function () {
  598. wx.hideLoading()
  599. }, 2000)
  600. this.videoAd.show().catch((err) => {
  601. console.log('激励视频播放失败', err)
  602. wx.showToast({
  603. title: '激励视频广告播放失败,请稍后再试',
  604. icon: 'none'
  605. })
  606. })
  607. },
  608. // 进入页面插屏
  609. interstitalEnterPageShow() {
  610. insertAdInterval = setTimeout(() => {
  611. console.log('详情页进入页面插屏调用')
  612. this.insertAdType = 2
  613. this.interstitalPlayFn()
  614. }, 2000)
  615. },
  616. // 插屏广告加载
  617. interstitalLoad() {
  618. // const _this = this
  619. interstitialAd = wx.createInterstitialAd({
  620. adUnitId: apple.ads.insertAdId
  621. })
  622. interstitialAd.onLoad(() => {
  623. console.log('详情视频页插屏广告 load success')
  624. })
  625. interstitialAd.onError((err) => {
  626. console.log('详情视频页插屏广告onError', err)
  627. })
  628. interstitialAd.onClose(() => {
  629. console.log('详情视频页插屏广告onClose')
  630. this.makeVideoPlay()
  631. })
  632. },
  633. // 插屏广告展示
  634. interstitalPlayFn() {
  635. // const _this = this
  636. if (interstitialAd) {
  637. interstitialAd.show().then(() => {
  638. console.log('插屏显示,视频暂停')
  639. if (this.insertAdType !== 1) {
  640. setTimeout(() => {
  641. this.makeVideoPause()
  642. }, 500)
  643. }
  644. }).catch((err) => {
  645. if (this.insertAdType !== 1) {
  646. setTimeout(() => {
  647. this.makeVideoPlay()
  648. }, 1000)
  649. }
  650. console.error('详情页插屏显示错误', err)
  651. })
  652. }
  653. },
  654. })