video.js 15 KB

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