// components/my-video/index.ts import { AES_KEY, AES_IV } from "../../env"; import { request } from "../../utils/request"; import { aesEncrypt } from "../../utils/crypto"; interface VideoData { screen: boolean; playing: boolean; currentTime: number; isReady: boolean; sendDurationTimer: number | null; lastUpdateTime: number; isConnected: boolean; // 新增网络连接状态字段 } interface MyVideoComponent { data: VideoData; videoContext: WechatMiniprogram.VideoContext | null; pauseVideoHandler?: () => void; resumeVideoHandler?: () => void; setData: (d: Partial, cb?: () => void) => void; initNetworkListener: () => void; startSendProgress: () => void; clearSendProgressTimer: () => void; sendProgress: () => Promise; } Component({ properties: { src: String, backgroundUrl: String, }, data: { screen: false, playing: false, currentTime: 0, isReady: false, isConnected: false, sendDurationTimer: 0, lastUpdateTime: 0, } as VideoData, lifetimes: { attached(this: MyVideoComponent) { this.initNetworkListener(); // 获取 videoContext 并初始化 this.videoContext = wx.createVideoContext("videoPlayer", this as any); const pauseVideo = () => { if (this.data.playing) { wx.setStorageSync("interruptedTime", this.data.currentTime); this.videoContext?.pause(); this.setData({ playing: false }); this.clearSendProgressTimer(); } }; const resumeVideo = () => { if (!this.videoContext) { this.videoContext = wx.createVideoContext("videoPlayer", this as any); } this.videoContext.pause(); }; this.pauseVideoHandler = pauseVideo; this.resumeVideoHandler = resumeVideo; wx.onAudioInterruptionBegin(this.pauseVideoHandler); wx.onAudioInterruptionEnd(this.resumeVideoHandler); wx.onAppHide(this.pauseVideoHandler); wx.onAppShow(this.resumeVideoHandler); }, detached(this: MyVideoComponent) { if (this.pauseVideoHandler) { wx.offAudioInterruptionBegin(this.pauseVideoHandler); wx.offAppHide(this.pauseVideoHandler); } if (this.resumeVideoHandler) { wx.offAudioInterruptionEnd(this.resumeVideoHandler); wx.offAppShow(this.resumeVideoHandler); } this.clearSendProgressTimer(); this.videoContext = null; }, }, methods: { initNetworkListener(this: MyVideoComponent) { // 首先获取当前网络状态 wx.getNetworkType({ success: (res) => { this.setData({ isConnected: res.networkType !== "none", }); }, }); wx.onNetworkStatusChange( (res: WechatMiniprogram.OnNetworkStatusChangeCallbackResult) => { this.setData({ isConnected: res.isConnected, }); if (!res.isConnected && this.data.playing) { this.videoContext?.pause(); this.setData({ playing: false }); wx.showToast({ title: "网络连接已断开", icon: "none", duration: 2000, }); } if (res.isConnected) { wx.showToast({ title: "网络已连接", icon: "success", duration: 1500, }); } } ); }, handleLoadedMetadata(this: MyVideoComponent) { this.setData({ isReady: true }); }, handlePosterTap(this: MyVideoComponent) { if (!this.videoContext || !this.data.isReady) return; if (!this.data.isConnected) { wx.showToast({ title: "网络连接异常,请检查网络设置", icon: "none", duration: 2000, }); return; } const newPlaying = !this.data.playing; const resumeTime = (wx.getStorageSync("interruptedTime") as number) || this.data.currentTime || (wx.getStorageSync("currentTime") as number) || 0; if (newPlaying) { if (resumeTime > 0) { this.videoContext.seek(resumeTime); } this.videoContext.play(); if (!this.data.sendDurationTimer) { this.startSendProgress(); } } else { this.videoContext.pause(); this.clearSendProgressTimer(); } this.setData({ playing: newPlaying }); wx.removeStorageSync("interruptedTime"); }, handlePause(this: MyVideoComponent) { wx.setStorageSync("interruptedTime", this.data.currentTime); this.setData({ playing: false }); this.clearSendProgressTimer(); }, handleTimeUpdate( this: MyVideoComponent, e: WechatMiniprogram.VideoTimeUpdate ) { const now = Date.now(); const currentTime = Math.floor(e.detail.currentTime ?? 0); if (now - this.data.lastUpdateTime >= 1000) { this.setData({ currentTime }); (this.data as VideoData).lastUpdateTime = now; wx.setStorageSync("currentTime", currentTime); } }, handleEnded(this: MyVideoComponent) { this.sendProgress(); wx.setStorageSync("ended", true); this.setData({ playing: false, currentTime: 0 }); wx.removeStorageSync("currentTime"); wx.removeStorageSync("interruptedTime"); this.clearSendProgressTimer(); }, handleFullscreenChange(this: MyVideoComponent) { const newScreen = !this.data.screen; if (newScreen) { this.videoContext?.requestFullScreen({ direction: 90 }); } else { this.videoContext?.exitFullScreen(); } this.setData({ screen: newScreen }); }, handleError(this: MyVideoComponent, e: WechatMiniprogram.VideoError) { console.error("播放错误:", e.detail.errMsg); this.setData({ playing: false }); }, startSendProgress(this: MyVideoComponent) { this.clearSendProgressTimer(); const timer = setInterval(() => { this.sendProgress(); }, 15000) as unknown as number; this.setData({ sendDurationTimer: timer }); }, clearSendProgressTimer(this: MyVideoComponent) { const t = this.data.sendDurationTimer; if (t) { clearInterval(t); this.setData({ sendDurationTimer: 0 }); } }, async sendProgress(this: MyVideoComponent) { try { const openId = wx.getStorageSync("openId"); const code = wx.getStorageSync("code"); const state = wx.getStorageSync("state"); const seeId = wx.getStorageSync("seeId"); const duration = this.data.currentTime + 1; if (code) { const params = { openId, code, duration }; await request({ options: { url: `/app/video-watch/inspect`, data: aesEncrypt(JSON.stringify(params), AES_KEY, AES_IV), }, isLoading: false, }); } else { const params = { openId, state, seeId, duration }; await request({ options: { url: `/app/watch/inspect`, data: aesEncrypt(JSON.stringify(params), AES_KEY, AES_IV), }, isLoading: false, }); } } catch (err) { console.error("上报观看进度失败:", err); } }, }, });