Browse Source

feat: 添加视频帧提取功能,解决视频-only商品分享卡片为空的问题

pull/11/head
Trae AI 2 months ago
parent
commit
fc8d070742
  1. 220
      pages/goods-detail/goods-detail.js
  2. 20
      pages/goods-detail/goods-detail.wxml

220
pages/goods-detail/goods-detail.js

@ -429,10 +429,36 @@ Page({
sharePath += `&region=${regionParam}`; sharePath += `&region=${regionParam}`;
} }
// 确定分享图片
let imageUrl = '/images/你有好蛋.png'; // 默认值
// 检查是否为纯视频商品
const mediaItems = goodsDetail.mediaItems || [];
const hasVideo = mediaItems.some(item => item.type === 'video');
const hasImage = mediaItems.some(item => item.type === 'image');
if (hasVideo && !hasImage) {
// 纯视频商品:使用提取的封面或默认图片
if (this.data.videoCoverUrl) {
imageUrl = this.data.videoCoverUrl;
console.log('使用提取的视频封面分享:', imageUrl);
} else {
console.log('视频封面尚未提取完成,使用默认图片');
// 尝试立即提取视频封面
const videoItems = mediaItems.filter(item => item.type === 'video');
if (videoItems.length > 0) {
this.extractVideoFirstFrame(videoItems[0].url);
}
}
} else {
// 有图片的商品:使用原有的图片选择逻辑
imageUrl = getShareImageUrl(goodsDetail);
}
return { return {
title: title, title: title,
path: sharePath, path: sharePath,
imageUrl: getShareImageUrl(goodsDetail) imageUrl: imageUrl
} }
}, },
@ -466,10 +492,34 @@ Page({
const contactQuery = queryParams.join('&'); const contactQuery = queryParams.join('&');
// 确定分享图片
let imageUrl = '/images/你有好蛋.png';
const mediaItems = goodsDetail.mediaItems || [];
const hasVideo = mediaItems.some(item => item.type === 'video');
const hasImage = mediaItems.some(item => item.type === 'image');
if (hasVideo && !hasImage) {
// 纯视频商品:使用提取的封面或默认图片
if (this.data.videoCoverUrl) {
imageUrl = this.data.videoCoverUrl;
console.log('使用提取的视频封面分享:', imageUrl);
} else {
console.log('视频封面尚未提取完成,使用默认图片');
// 尝试立即提取视频封面
const videoItems = mediaItems.filter(item => item.type === 'video');
if (videoItems.length > 0) {
this.extractVideoFirstFrame(videoItems[0].url);
}
}
} else {
imageUrl = getShareImageUrl(goodsDetail);
}
return { return {
title: title, title: title,
query: contactQuery, query: contactQuery,
imageUrl: getShareImageUrl(goodsDetail) imageUrl: imageUrl
} }
}, },
@ -500,6 +550,11 @@ Page({
favoriteGoods: [], // 收藏商品数据 favoriteGoods: [], // 收藏商品数据
loadingHome: false, // 首页数据加载状态 loadingHome: false, // 首页数据加载状态
loadingFavorite: false, // 收藏数据加载状态 loadingFavorite: false, // 收藏数据加载状态
// 新增以下字段:
videoCoverUrl: null, // 视频封面图片URL
isExtractingCover: false, // 是否正在提取封面
videoCoverCache: {}, // 视频封面缓存 {videoUrl: coverUrl}
videoSrcForSnapshot: '', // 用于截图的视频URL
}, },
onLoad: function (options) { onLoad: function (options) {
@ -649,6 +704,143 @@ Page({
}); });
}, },
// ========== 新增:视频封面提取相关方法 ==========
/**
* 检查并提取视频封面
*/
async checkAndExtractVideoCover() {
const goodsDetail = this.data.goodsDetail;
const mediaItems = goodsDetail.mediaItems || [];
// 1. 检查是否为纯视频商品
const hasVideo = mediaItems.some(item => item.type === 'video');
const hasImage = mediaItems.some(item => item.type === 'image');
if (!hasVideo || hasImage) {
// 不是纯视频商品,不需要提取
return;
}
// 2. 获取第一个视频
const videoItems = mediaItems.filter(item => item.type === 'video');
if (videoItems.length === 0) return;
const firstVideo = videoItems[0];
const videoUrl = firstVideo.url;
// 3. 检查缓存
const cachedCover = this.data.videoCoverCache[videoUrl];
if (cachedCover) {
console.log('使用缓存的视频封面');
this.setData({
videoCoverUrl: cachedCover
});
return;
}
// 4. 开始提取
await this.extractVideoFirstFrame(videoUrl);
},
/**
* 提取视频第一帧
*/
async extractVideoFirstFrame(videoUrl) {
if (!videoUrl || this.data.isExtractingCover) {
return null;
}
console.log('开始提取视频封面:', videoUrl);
this.setData({ isExtractingCover: true });
try {
// 方法1:使用snapshot API(推荐)
const coverUrl = await this.captureVideoFrame(videoUrl);
if (coverUrl && coverUrl !== '/images/你有好蛋.png') {
// 更新缓存
const newCache = { ...this.data.videoCoverCache };
newCache[videoUrl] = coverUrl;
this.setData({
videoCoverUrl: coverUrl,
videoCoverCache: newCache,
isExtractingCover: false
});
console.log('视频封面提取成功:', coverUrl);
return coverUrl;
}
} catch (error) {
console.error('提取视频封面失败:', error);
}
// 降级到默认图片
this.setData({
videoCoverUrl: '/images/你有好蛋.png',
isExtractingCover: false
});
return '/images/你有好蛋.png';
},
/**
* 捕获视频帧从真实视频中提取帧
*/
captureVideoFrame(videoUrl) {
return new Promise((resolve, reject) => {
console.log('尝试从视频中提取真实帧:', videoUrl);
try {
// 检查当前环境是否为预览环境
const isPreview = __wxConfig.envVersion === 'develop' || __wxConfig.envVersion === 'trial';
console.log('当前环境:', isPreview ? '预览环境' : '生产环境');
// 检查视频URL是否来自阿里云OSS
// 如果是,可以使用阿里云OSS的视频处理参数来获取视频帧
if (videoUrl.includes('aliyuncs.com')) {
console.log('检测到阿里云OSS视频,使用URL参数获取视频帧');
// 构建带参数的URL,用于获取视频第一帧
// 添加阿里云OSS视频处理参数:x-oss-process=video/snapshot,t_0,f_jpg
// t_0表示获取第0秒的帧,f_jpg表示输出为jpg格式
const frameUrl = videoUrl + (videoUrl.includes('?') ? '&' : '?') + 'x-oss-process=video/snapshot,t_0,f_jpg';
console.log('构建的视频帧URL:', frameUrl);
// 使用wx.downloadFile下载视频帧
wx.downloadFile({
url: frameUrl,
success: (res) => {
console.log('视频帧下载响应:', res);
if (res.statusCode === 200) {
console.log('视频帧下载成功:', res.tempFilePath);
resolve(res.tempFilePath);
} else {
console.error('视频帧下载失败,状态码:', res.statusCode);
// 降级方案:使用默认图片
resolve('/images/你有好蛋.png');
}
},
fail: (err) => {
console.error('视频帧下载失败:', err);
// 降级方案:使用默认图片
resolve('/images/你有好蛋.png');
}
});
} else {
// 如果不是阿里云OSS视频,使用默认图片
console.log('非阿里云OSS视频,使用默认图片');
resolve('/images/你有好蛋.png');
}
} catch (error) {
console.error('提取视频帧异常:', error);
// 降级方案:使用默认图片
resolve('/images/你有好蛋.png');
}
});
},
loadGoodsDetail: function (productId, preloadedData = null, contactFromShare = null) { loadGoodsDetail: function (productId, preloadedData = null, contactFromShare = null) {
// 首先显示预加载的数据,确保UI快速响应 // 首先显示预加载的数据,确保UI快速响应
if (preloadedData) { if (preloadedData) {
@ -982,15 +1174,21 @@ Page({
} }
this.setData({ this.setData({
goodsDetail: formattedGoods, goodsDetail: formattedGoods,
displayRegion: displayRegion, displayRegion: displayRegion,
isFavorite: preloadedFavoriteStatus // 优先使用预加载数据中的收藏状态 isFavorite: preloadedFavoriteStatus, // 优先使用预加载数据中的收藏状态
}); videoCoverUrl: null // 重置封面URL
});
// 只有当没有预加载的收藏状态时,才从服务器加载
if (!preloadedData || preloadedData.isFavorite === undefined) { // 商品加载完成后检查并提取视频封面
this.loadGoodsFavoriteStatus(productIdStr); setTimeout(() => {
} this.checkAndExtractVideoCover();
}, 500);
// 只有当没有预加载的收藏状态时,才从服务器加载
if (!preloadedData || preloadedData.isFavorite === undefined) {
this.loadGoodsFavoriteStatus(productIdStr);
}
} else { } else {
wx.showToast({ wx.showToast({
title: '获取商品详情失败', title: '获取商品详情失败',

20
pages/goods-detail/goods-detail.wxml

@ -1,5 +1,25 @@
<!-- pages/goods-detail/goods-detail.wxml --> <!-- pages/goods-detail/goods-detail.wxml -->
<view class="goods-detail-page"> <view class="goods-detail-page">
<!-- 在页面最顶部添加以下代码 -->
<!-- 隐藏的video组件,用于视频封面截图 -->
<video
id="videoSnapshotHelper"
src="{{videoSrcForSnapshot}}"
style="position: absolute; top: -9999px; width: 300px; height: 300px;"
autoplay="{{false}}"
muted="{{true}}"
show-center-play-btn="{{false}}"
show-play-btn="{{false}}"
object-fit="contain"
></video>
<!-- 隐藏的canvas组件,用于绘制视频帧 -->
<canvas
id="videoCanvas"
canvas-id="videoCanvas"
style="position: absolute; top: -9999px; width: 300px; height: 300px;"
></canvas>
<!-- 商品详情内容 --> <!-- 商品详情内容 -->
<view class="goods-detail-content"> <view class="goods-detail-content">
<!-- 商品媒体轮播(支持图片和视频) --> <!-- 商品媒体轮播(支持图片和视频) -->

Loading…
Cancel
Save