video.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. // pages/collect-video/video.js
  2. import {
  3. getCollectDetail,
  4. getTemplateConfig,
  5. getUuid,
  6. getCollectRealVideoUrl,
  7. getRecCollectList,
  8. } from "../../api/api";
  9. import { rnd, SendEvent, rndone } from "../../utils/util";
  10. import { apple } from "../../config/config";
  11. const app = getApp();
  12. let interstitialAd = null;
  13. Page({
  14. data: {
  15. loading: false,
  16. hasMore: true,
  17. // 状态栏高度
  18. statusBarHeight: 20,
  19. navHeight: 64, // 导航栏高度 = 状态栏 + 44
  20. navStyle: "",
  21. listStyle: "",
  22. ads: null,
  23. globalConfig: null,
  24. hasLoadAdConfig: false,
  25. templateAdId: "",
  26. adErrorField: {
  27. videoError: false,
  28. bannerError: false,
  29. templateError: false,
  30. },
  31. collectId: "", // 合集id
  32. tagId: "", // 合集标签id
  33. eps: [], // 合集视频列表
  34. playingEp: 0, // 正在播放集
  35. videoDetail: null,
  36. isPlaying: false,
  37. videoStatus: 1,
  38. duration: 0, // 视频总时长
  39. currentTime: 0, // 当前播放时间
  40. },
  41. onLoad(options) {
  42. console.log("打开视频合集页面--------------------------");
  43. // wx.setStorageSync("jump", parseInt(options.jump)); // 是否跳转阅读器
  44. this.autoLogin(options.promotionid);
  45. if (!app.globalData.uuid) {
  46. this.getUUID().then((uuid) => {
  47. // getApp().globalData.uuid = uuid
  48. console.log("登录成功");
  49. });
  50. }
  51. this.handleEntrace(options);
  52. this.setData({
  53. collectId: options.collectId,
  54. templateAdId: apple.ads.templateAdId,
  55. hasLoadAdConfig: true,
  56. });
  57. getTemplateConfig({
  58. box_type: 73,
  59. uuid: app.globalData.uuid,
  60. }).then((res) => {
  61. this.setData({
  62. globalConfig: res.globalConfig,
  63. });
  64. this.interstitalLoad();
  65. this.rewardVideoLoad();
  66. setTimeout(() => {
  67. this.interstitalPlayFn();
  68. }, 5000);
  69. // 获取合集视频详情
  70. this.getCollectVideoDetail();
  71. this.loadVideoList();
  72. });
  73. // 获取状态栏高度
  74. this.getSystemInfo();
  75. // this.handleEntrace(options);
  76. },
  77. // 自动登录
  78. async autoLogin(promotionid) {
  79. try {
  80. await getApp().login(promotionid);
  81. } catch (error) {
  82. console.error("登录失败:", error);
  83. }
  84. },
  85. onShow() {},
  86. handleEntrace(options) {
  87. // console.log("点击入口按钮");
  88. // if (!options || Object.keys(options).length === 0) {
  89. // return;
  90. // }
  91. // console.log("入口:", options);
  92. // try {
  93. // } catch (e) {
  94. // console.log("处理不同场景进入小程序出错", error);
  95. // }
  96. },
  97. clickBack() {
  98. wx.switchTab({
  99. url: "/pages/video/video",
  100. });
  101. },
  102. // 获取视频合集详情
  103. getCollectVideoDetail() {
  104. // 显示加载中提示
  105. wx.showLoading({
  106. title: "加载中...",
  107. mask: true,
  108. });
  109. getCollectDetail(this.data.collectId)
  110. .then((res) => {
  111. if (res && res.video.length > 0) {
  112. res.video
  113. .sort((a, b) => a.sort < b.sort)
  114. .map((e, i) => {
  115. // -------- 锁定判断 --------
  116. if (this.isCollectNeedLock()) {
  117. // set lock status
  118. if (i + 1 >= res.lockStart) {
  119. e.lock = true;
  120. } else {
  121. e.lock = false;
  122. }
  123. } else {
  124. e.lock = false;
  125. }
  126. return e;
  127. });
  128. this.setData({
  129. eps: res.video,
  130. tagId: res.tagId,
  131. playingEp: 0,
  132. videoDetail: res.video[0],
  133. });
  134. // 默认播放第一集
  135. this.getVideoPlayUrl();
  136. }
  137. })
  138. .catch((err) => {
  139. console.error(err);
  140. })
  141. .then(() => {
  142. wx.hideLoading();
  143. });
  144. },
  145. // 判断合集是否需要锁定
  146. isCollectNeedLock() {
  147. // 获取当前日期(格式:YYYY-MM-DD)
  148. const today = new Date().toISOString().split("T")[0];
  149. // 从 localStorage 获取数据
  150. const hadRewardNum = parseInt(
  151. wx.getStorageSync("had_reward_num") || "0",
  152. 10,
  153. );
  154. const unlockCollectIds = JSON.parse(
  155. wx.getStorageSync("unlock_collect_ids") || "[]",
  156. );
  157. const lastCheckDate = wx.getStorageSync("last_check_date");
  158. if (this.data.globalConfig.isShowReward === 1) {
  159. // 检查是否需要重置(新的一天)
  160. if (lastCheckDate !== today) {
  161. // 重置解锁数据
  162. wx.setStorageSync("had_reward_num", "0");
  163. wx.setStorageSync("unlock_collect_ids", "[]");
  164. wx.setStorageSync("last_check_date", today);
  165. return true; // 新的一天,当前视频未解锁
  166. }
  167. // 判断解锁状态
  168. // 1. 如果当天广告观看次数 >= 3,所有视频解锁
  169. if (hadRewardNum >= 3) {
  170. return false;
  171. }
  172. if (unlockCollectIds.includes(this.data.collectId)) {
  173. return false;
  174. }
  175. return true;
  176. } else {
  177. return false;
  178. }
  179. },
  180. async getVideoPlayUrl() {
  181. try {
  182. const res = await getCollectRealVideoUrl(this.data.videoDetail.url);
  183. this.setData({
  184. "videoDetail.playUrl": res.url_info[0].url,
  185. });
  186. setTimeout(() => {
  187. const videoContext = wx.createVideoContext("my-video");
  188. videoContext.play();
  189. }, 200);
  190. // 获取视频实例并播放
  191. } catch (error) {}
  192. },
  193. // 点击集
  194. clickEp(e) {
  195. const { ep, idx } = e.currentTarget.dataset;
  196. if (idx === this.data.playingEp) return;
  197. this.setData({
  198. playingEp: idx,
  199. videoDetail: ep,
  200. });
  201. SendEvent("click_collection_video", {
  202. cid: this.data.collectId,
  203. video_id: this.data.eps[this.data.playingEp].id,
  204. tag_id: this.data.tagId,
  205. });
  206. if (!this.data.videoDetail.lock) {
  207. this.getVideoPlayUrl();
  208. }
  209. },
  210. clickUnlock() {
  211. this.rewardVideoPlayFn();
  212. },
  213. /**
  214. * 获取uuid
  215. */
  216. getUUID() {
  217. return new Promise((resolve, reject) => {
  218. wx.login({
  219. success: (res) => {
  220. console.log("wxlogin:", res);
  221. let body = {
  222. code: res.code,
  223. source_type: 1,
  224. source_id: "",
  225. };
  226. getUuid(body)
  227. .then(async (uuidres) => {
  228. console.log("登录成功:", uuidres);
  229. app.globalData.uuid = uuidres.uuid;
  230. app.globalData.openId = uuidres.open_id;
  231. console.log("登录后的openid:", uuidres.open_id);
  232. console.log("登录成功:", app.globalData);
  233. SendEvent("login_shanhu", { uuid: uuidres.uuid });
  234. resolve(res.uuid);
  235. })
  236. .catch((err) => {
  237. reject(err);
  238. });
  239. },
  240. });
  241. });
  242. },
  243. // 获取系统信息
  244. getSystemInfo() {
  245. const systemInfo = wx.getSystemInfoSync();
  246. const statusBarHeight = systemInfo.statusBarHeight;
  247. const navHeight = statusBarHeight + 44;
  248. this.setData({
  249. statusBarHeight: statusBarHeight,
  250. navHeight: navHeight,
  251. navStyle: `height:${navHeight}px`,
  252. listStyle: `top:${navHeight}px`,
  253. });
  254. },
  255. // 加载视频列表
  256. async loadVideoList() {
  257. this.setData({
  258. loading: true,
  259. });
  260. try {
  261. const params = {
  262. tagIdList: this.data.globalConfig.tagIds.join(","),
  263. count: this.data.globalConfig.recommendListNum,
  264. id: Number(this.data.collectId),
  265. };
  266. const res = await getRecCollectList(params);
  267. res.forEach((e) => {
  268. e.adType = "video";
  269. e.views = rnd(1, 10);
  270. });
  271. this.setData({
  272. videoList: res,
  273. loading: false,
  274. hasMore: false,
  275. });
  276. } catch (error) {
  277. console.error("加载视频列表失败:", error);
  278. }
  279. },
  280. // 视频播放
  281. makeVideoPlay() {
  282. if (!this.data.videoDetail.lock && this.data.videoStatus == 2) {
  283. const videoContext = wx.createVideoContext("my-video");
  284. videoContext.play();
  285. }
  286. },
  287. // 视频暂停
  288. makeVideoPause() {
  289. if (this.data.videoStatus == 1) {
  290. const videoContext = wx.createVideoContext("my-video");
  291. videoContext.pause();
  292. }
  293. },
  294. // 视频播放事件
  295. onVideoPlay() {
  296. this.setData({
  297. isPlaying: true,
  298. videoStatus: 1,
  299. });
  300. // 可以添加播放埋点
  301. SendEvent("collection_video_play", {
  302. cid: this.data.collectId,
  303. video_id: this.data.eps[this.data.playingEp].id,
  304. tag_id: this.data.tagId,
  305. });
  306. },
  307. // 视频暂停事件
  308. onVideoPause() {
  309. this.setData({
  310. isPlaying: false,
  311. videoStatus: 2,
  312. });
  313. },
  314. // 视频播放结束事件
  315. onVideoEnd() {
  316. this.setData({
  317. isPlaying: false,
  318. videoStatus: 0,
  319. });
  320. const nextEp = this.data.playingEp + 1;
  321. // 最后一集播放完,随机播放下一个合集
  322. if (nextEp === this.data.eps.length && this.data.videoList.length > 0) {
  323. const rndCollect = rndone(this.data.videoList);
  324. this.setData({
  325. collectId: rndCollect.id,
  326. });
  327. this.getCollectVideoDetail();
  328. } else {
  329. // 切换下一集
  330. if (nextEp <= this.data.eps.length - 1) {
  331. this.setData({
  332. playingEp: nextEp,
  333. videoDetail: this.data.eps[nextEp],
  334. });
  335. if (!this.data.videoDetail.lock) {
  336. this.getVideoPlayUrl();
  337. }
  338. }
  339. }
  340. },
  341. // 视频时间更新事件
  342. onVideoUpdate(e) {
  343. const { currentTime, duration } = e.detail;
  344. this.setData({
  345. currentTime,
  346. duration,
  347. });
  348. },
  349. // 视频元数据加载完成
  350. onVideoLoaded(e) {
  351. console.log("视频元数据加载完成", e.detail);
  352. },
  353. /* ------------------ 广告相关 ----------------- */
  354. /**
  355. * 标记广告位置
  356. * @param {} arr
  357. * @returns
  358. */
  359. formatVideoAdList(arr) {
  360. // 原生广告间隔,激励广告间隔
  361. let templateGap = 0,
  362. rewardGap = 0;
  363. console.log(this.data.globalConfig);
  364. if (this.data.globalConfig.isHomeTemplateAd === 1) {
  365. templateGap = this.data.globalConfig.homeTemplateAdInterval;
  366. } else {
  367. templateGap = 0;
  368. }
  369. if (this.data.globalConfig.isHomeRewardAd) {
  370. rewardGap = this.data.globalConfig.homeRewardAdInterval;
  371. } else {
  372. rewardGap = 0;
  373. }
  374. console.log(
  375. "原生间隔templateGap",
  376. templateGap,
  377. "激励间隔rewardGap",
  378. rewardGap,
  379. );
  380. // 第二个固定是原生广告
  381. let result = [
  382. arr.shift(),
  383. {
  384. adType: "templateAd",
  385. },
  386. ];
  387. // 辅助计算间隔变量
  388. let markIndex = 0;
  389. let templateAdFlag = true;
  390. let rewardAdFlag = true;
  391. do {
  392. markIndex += 1;
  393. if (templateGap > 0) {
  394. if (templateAdFlag && markIndex - 1 === templateGap) {
  395. markIndex = 0;
  396. result.push({
  397. adType: "templateAd",
  398. });
  399. templateAdFlag = false;
  400. rewardAdFlag = true;
  401. continue;
  402. }
  403. } else {
  404. rewardAdFlag = true;
  405. }
  406. if (rewardGap > 0) {
  407. if (rewardAdFlag && markIndex - 1 === rewardGap) {
  408. markIndex = 0;
  409. templateAdFlag = true;
  410. rewardAdFlag = false;
  411. let rewardItem = arr.shift();
  412. rewardItem.lock = true;
  413. result.push(rewardItem);
  414. console.log("标记激励项:", rewardItem);
  415. continue;
  416. }
  417. } else {
  418. templateAdFlag = true;
  419. }
  420. result.push(arr.shift());
  421. } while (arr.length > 0);
  422. return result;
  423. },
  424. // 点击合集
  425. clickCollect(e) {
  426. const { id, info, index } = e.currentTarget.dataset;
  427. this.setData({
  428. collectId: id,
  429. tagId: info.tagId,
  430. });
  431. SendEvent("click_collection", {
  432. cid: this.data.collectId,
  433. tag_id: this.data.tagId,
  434. });
  435. console.log(id, info, index, "合集详情");
  436. this.getCollectVideoDetail();
  437. },
  438. // 分享给朋友
  439. onShareAppMessage(e) {},
  440. // 激励视频加载
  441. rewardVideoLoad() {
  442. if (this.data.globalConfig.isShowReward !== 1) return;
  443. if (wx.createRewardedVideoAd) {
  444. let adId = apple.ads.rewardAdId;
  445. this.videoAd = wx.createRewardedVideoAd({
  446. adUnitId: adId,
  447. });
  448. this.videoAd.onLoad(() => {
  449. console.log("首页激励加载成功");
  450. });
  451. this.videoAd.onError((err) => {
  452. console.log("首页激励失败", err);
  453. });
  454. this.videoAd.onClose((res) => {
  455. // 用户点击了【关闭广告】按钮
  456. if (res && res.isEnded) {
  457. // 发放奖励
  458. const hadRewardNum = parseInt(
  459. wx.getStorageSync("had_reward_num") || "0",
  460. 10,
  461. );
  462. const unlockCollectIds = JSON.parse(
  463. wx.getStorageSync("unlock_collect_ids") || "[]",
  464. );
  465. wx.setStorageSync("had_reward_num", hadRewardNum + 1);
  466. wx.setStorageSync(
  467. "unlock_collect_ids",
  468. JSON.stringify([...unlockCollectIds, this.data.collectId]),
  469. );
  470. // 解锁当前合集,播放视频
  471. this.data.eps = this.data.eps.map((e) => {
  472. e.lock = false;
  473. return e;
  474. });
  475. this.setData({
  476. "videoDetail.lock": false,
  477. eps: this.data.eps,
  478. });
  479. SendEvent("unlock_collection", {
  480. cid: this.data.collectId,
  481. video_id: this.data.eps[this.playingEp].id,
  482. // tag_id: this.data.tagId,
  483. ad_num: hadRewardNum + 1,
  484. });
  485. this.getVideoPlayUrl();
  486. } else {
  487. SendEvent("unlock_collection_fail", {
  488. cid: this.data.collectId,
  489. video_id: this.data.eps[this.playingEp].id,
  490. });
  491. }
  492. });
  493. }
  494. },
  495. // 激励视频展示
  496. rewardVideoPlayFn() {
  497. if (this.data.globalConfig.isShowReward !== 1) return;
  498. wx.showLoading({
  499. title: "解锁视频中",
  500. });
  501. this.videoAd
  502. .show()
  503. .then(() => {
  504. console.log("激励视频播放成功");
  505. })
  506. .catch((err) => {
  507. console.log("激励视频播放失败", err);
  508. })
  509. .then(() => {
  510. wx.hideLoading();
  511. });
  512. },
  513. // 插屏广告加载
  514. interstitalLoad() {
  515. if (this.data.globalConfig.isShowInsert !== 1) return;
  516. let adId = apple.ads.insertAdId;
  517. interstitialAd = wx.createInterstitialAd({
  518. adUnitId: adId,
  519. });
  520. interstitialAd.onLoad(() => {
  521. console.log("合集视频页插屏广告onLoad");
  522. });
  523. interstitialAd.onError((err) => {
  524. console.log("合集视频页插屏广告onError", err);
  525. });
  526. interstitialAd.onClose(() => {
  527. console.log("合集视频页插屏广告onClose");
  528. this.makeVideoPlay();
  529. });
  530. },
  531. // 插屏广告展示
  532. interstitalPlayFn() {
  533. if (this.data.globalConfig.isShowInsert !== 1) return;
  534. if (interstitialAd) {
  535. interstitialAd
  536. .show()
  537. .then(() => {
  538. setTimeout(() => {
  539. this.makeVideoPause();
  540. }, 500);
  541. })
  542. .catch((err) => {
  543. console.error("合集视频页插屏显示错误", err);
  544. });
  545. }
  546. },
  547. });