chore: 优化检索效率
This commit is contained in:
parent
5abb5a6836
commit
2505df0182
@ -50,85 +50,67 @@ const MediaStatsScreen = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 设置时间范围
|
||||
// 2. 设置时间范围,直接使用Date对象
|
||||
const dateRange = getDateRange(timeRange);
|
||||
const createdAfter = dateRange ? Math.floor(dateRange.start.getTime() / 1000) : 0;
|
||||
const endTime = dateRange?.end ? Math.floor(dateRange.end.getTime() / 1000) : undefined;
|
||||
|
||||
// 3. 分页获取媒体资源,每次10条
|
||||
let hasNextPage = true;
|
||||
let after = undefined;
|
||||
// 3. 分页获取媒体资源
|
||||
let allAssets: MediaLibrary.Asset[] = [];
|
||||
const pageSize = 10; // 每次获取10条
|
||||
let hasNextPage = true;
|
||||
let after: MediaLibrary.AssetRef | undefined = undefined;
|
||||
const pageSize = 100; // 增加每次获取的数量以提高效率
|
||||
|
||||
while (hasNextPage) {
|
||||
const media = await MediaLibrary.getAssetsAsync({
|
||||
first: pageSize,
|
||||
after,
|
||||
mediaType: ['photo', 'video', 'audio', 'unknown'],
|
||||
sortBy: 'creationTime', // 按创建时间降序,最新的在前面
|
||||
createdAfter: Date.now() - 24 * 30 * 12 * 60 * 60 * 1000, // 时间戳(毫秒)
|
||||
createdBefore: Date.now(), // 时间戳(毫秒)
|
||||
sortBy: ['creationTime'],
|
||||
mediaType: ['photo', 'video', 'audio'],
|
||||
createdAfter: dateRange?.start,
|
||||
createdBefore: dateRange?.end,
|
||||
});
|
||||
|
||||
// 如果没有数据,直接退出
|
||||
if (media.assets.length === 0) {
|
||||
break;
|
||||
if (media.assets.length > 0) {
|
||||
allAssets.push(...media.assets);
|
||||
}
|
||||
|
||||
// 检查每条记录是否在时间范围内
|
||||
for (const asset of media.assets) {
|
||||
const assetTime = asset.creationTime ? new Date(asset.creationTime).getTime() / 1000 : 0;
|
||||
|
||||
// 如果设置了结束时间,并且当前记录的时间早于开始时间,则停止
|
||||
if (endTime && assetTime > endTime) {
|
||||
continue; // 跳过这条记录
|
||||
}
|
||||
|
||||
// 如果设置了开始时间,并且当前记录的时间早于开始时间,则停止
|
||||
if (createdAfter && assetTime < createdAfter) {
|
||||
hasNextPage = false;
|
||||
break;
|
||||
}
|
||||
|
||||
allAssets.push(asset);
|
||||
}
|
||||
|
||||
// 更新游标和是否还有下一页
|
||||
hasNextPage = media.hasNextPage && media.assets.length === pageSize;
|
||||
hasNextPage = media.hasNextPage;
|
||||
after = media.endCursor;
|
||||
|
||||
// 如果没有更多数据或者已经获取了足够的数据
|
||||
if (!hasNextPage || allAssets.length >= 1000) {
|
||||
// 可选:增加一个最大获取上限,防止无限循环
|
||||
if (allAssets.length > 2000) {
|
||||
console.warn('已达到2000个媒体文件的上限');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`总共获取到 ${allAssets.length} 个媒体文件`);
|
||||
|
||||
// 4. 统计不同类型媒体的数量
|
||||
const stats: MediaStats = {
|
||||
total: allAssets.length,
|
||||
photos: 0,
|
||||
videos: 0,
|
||||
audios: 0,
|
||||
others: 0,
|
||||
byMonth: {},
|
||||
};
|
||||
// 4. 使用 reduce 进行统计,更高效
|
||||
const stats = allAssets.reduce<MediaStats>((acc, asset) => {
|
||||
acc.total++;
|
||||
switch (asset.mediaType) {
|
||||
case 'photo':
|
||||
acc.photos++;
|
||||
break;
|
||||
case 'video':
|
||||
acc.videos++;
|
||||
break;
|
||||
case 'audio':
|
||||
acc.audios++;
|
||||
break;
|
||||
default:
|
||||
acc.others++;
|
||||
break;
|
||||
}
|
||||
|
||||
allAssets.forEach(asset => {
|
||||
// 统计类型
|
||||
if (asset.mediaType === 'photo') stats.photos++;
|
||||
else if (asset.mediaType === 'video') stats.videos++;
|
||||
else if (asset.mediaType === 'audio') stats.audios++;
|
||||
else stats.others++;
|
||||
|
||||
// 按月份统计
|
||||
if (asset.creationTime) {
|
||||
const date = new Date(asset.creationTime);
|
||||
const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
|
||||
stats.byMonth[monthKey] = (stats.byMonth[monthKey] || 0) + 1;
|
||||
acc.byMonth[monthKey] = (acc.byMonth[monthKey] || 0) + 1;
|
||||
}
|
||||
return acc;
|
||||
}, {
|
||||
total: 0, photos: 0, videos: 0, audios: 0, others: 0, byMonth: {},
|
||||
});
|
||||
|
||||
setStats(stats);
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
// import * as ImageManipulator from 'expo-image-manipulator';
|
||||
// import * as VideoThumbnail from 'expo-video-thumbnails';
|
||||
|
||||
// export const extractVideoThumbnail = async (videoUri: string): Promise<{ uri: string; file: File }> => {
|
||||
// try {
|
||||
// // 获取视频的第一帧
|
||||
// const { uri: thumbnailUri } = await VideoThumbnail.getThumbnailAsync(
|
||||
// videoUri,
|
||||
// {
|
||||
// time: 1000, // 1秒的位置
|
||||
// quality: 0.8,
|
||||
// }
|
||||
// );
|
||||
|
||||
// // 转换为 WebP 格式
|
||||
// const manipResult = await ImageManipulator.manipulateAsync(
|
||||
// thumbnailUri,
|
||||
// [{ resize: { width: 800 } }], // 调整大小以提高性能
|
||||
// {
|
||||
// compress: 0.8,
|
||||
// format: ImageManipulator.SaveFormat.WEBP
|
||||
// }
|
||||
// );
|
||||
|
||||
// // 转换为 File 对象
|
||||
// const response = await fetch(manipResult.uri);
|
||||
// const blob = await response.blob();
|
||||
// const file = new File(
|
||||
// [blob],
|
||||
// `thumb_${Date.now()}.webp`,
|
||||
// { type: 'image/webp' }
|
||||
// );
|
||||
|
||||
// return { uri: manipResult.uri, file };
|
||||
// } catch (error) {
|
||||
// console.error('Error generating video thumbnail:', error);
|
||||
// throw new Error('无法生成视频缩略图: ' + (error instanceof Error ? error.message : String(error)));
|
||||
// }
|
||||
// };
|
||||
@ -1,138 +0,0 @@
|
||||
// /**
|
||||
// * 从视频文件中提取第一帧并返回为File对象
|
||||
// * @param videoFile 视频文件
|
||||
// * @returns 包含视频第一帧的File对象
|
||||
// */
|
||||
// export const extractVideoFirstFrame = (videoFile: File): Promise<File> => {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// const videoUrl = URL.createObjectURL(videoFile);
|
||||
// const video = document.createElement('video');
|
||||
// video.src = videoUrl;
|
||||
// video.crossOrigin = 'anonymous';
|
||||
// video.muted = true;
|
||||
// video.preload = 'metadata';
|
||||
|
||||
// video.onloadeddata = () => {
|
||||
// try {
|
||||
// // 设置视频时间到第一帧
|
||||
// video.currentTime = 0.1;
|
||||
// } catch (e) {
|
||||
// URL.revokeObjectURL(videoUrl);
|
||||
// reject(e);
|
||||
// }
|
||||
// };
|
||||
|
||||
// video.onseeked = () => {
|
||||
// try {
|
||||
// const canvas = document.createElement('canvas');
|
||||
// canvas.width = video.videoWidth;
|
||||
// canvas.height = video.videoHeight;
|
||||
// const ctx = canvas.getContext('2d');
|
||||
|
||||
// if (!ctx) {
|
||||
// throw new Error('无法获取canvas上下文');
|
||||
// }
|
||||
|
||||
// // 绘制视频帧到canvas
|
||||
// ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
// // 将canvas转换为DataURL
|
||||
// const dataUrl = canvas.toDataURL('image/jpeg');
|
||||
|
||||
// // 将DataURL转换为Blob
|
||||
// const byteString = atob(dataUrl.split(',')[1]);
|
||||
// const mimeString = dataUrl.split(',')[0].split(':')[1].split(';')[0];
|
||||
// const ab = new ArrayBuffer(byteString.length);
|
||||
// const ia = new Uint8Array(ab);
|
||||
// for (let i = 0; i < byteString.length; i++) {
|
||||
// ia[i] = byteString.charCodeAt(i);
|
||||
// }
|
||||
// const blob = new Blob([ab], { type: mimeString });
|
||||
|
||||
// // 创建File对象
|
||||
// const frameFile = new File(
|
||||
// [blob],
|
||||
// `${videoFile.name.replace(/\.[^/.]+$/, '')}_frame.jpg`,
|
||||
// { type: 'image/jpeg' }
|
||||
// );
|
||||
|
||||
// // 清理URL对象
|
||||
// URL.revokeObjectURL(videoUrl);
|
||||
// resolve(frameFile);
|
||||
// } catch (e) {
|
||||
// URL.revokeObjectURL(videoUrl);
|
||||
// reject(e);
|
||||
// }
|
||||
// };
|
||||
|
||||
// video.onerror = () => {
|
||||
// URL.revokeObjectURL(videoUrl);
|
||||
// reject(new Error('视频加载失败'));
|
||||
// };
|
||||
// });
|
||||
// };
|
||||
|
||||
// // 获取视频时长
|
||||
// export const getVideoDuration = (file: File): Promise<number> => {
|
||||
// return new Promise((resolve) => {
|
||||
// const video = document.createElement('video');
|
||||
// video.preload = 'metadata';
|
||||
|
||||
// video.onloadedmetadata = () => {
|
||||
// URL.revokeObjectURL(video.src);
|
||||
// resolve(video.duration);
|
||||
// };
|
||||
|
||||
// video.onerror = () => {
|
||||
// URL.revokeObjectURL(video.src);
|
||||
// resolve(0); // Return 0 if we can't get the duration
|
||||
// };
|
||||
|
||||
// video.src = URL.createObjectURL(file);
|
||||
// });
|
||||
// };
|
||||
|
||||
// // 根据 mp4 的url来获取视频时长
|
||||
// /**
|
||||
// * 根据视频URL获取视频时长
|
||||
// * @param videoUrl 视频的URL
|
||||
// * @returns 返回一个Promise,解析为视频时长(秒)
|
||||
// */
|
||||
// export const getVideoDurationFromUrl = async (videoUrl: string): Promise<number> => {
|
||||
// return await new Promise((resolve, reject) => {
|
||||
// // 创建临时的video元素
|
||||
// const video = document.createElement('video');
|
||||
|
||||
// // 设置为只加载元数据,不加载整个视频
|
||||
// video.preload = 'metadata';
|
||||
|
||||
// // 处理加载成功
|
||||
// video.onloadedmetadata = () => {
|
||||
// // 释放URL对象
|
||||
// URL.revokeObjectURL(video.src);
|
||||
// // 返回视频时长(秒)
|
||||
// resolve(video.duration);
|
||||
// };
|
||||
|
||||
// // 处理加载错误
|
||||
// video.onerror = () => {
|
||||
// URL.revokeObjectURL(video.src);
|
||||
// reject(new Error('无法加载视频'));
|
||||
// };
|
||||
|
||||
// // 处理网络错误
|
||||
// video.onabort = () => {
|
||||
// URL.revokeObjectURL(video.src);
|
||||
// reject(new Error('视频加载被中止'));
|
||||
// };
|
||||
|
||||
// // 设置视频源
|
||||
// video.src = videoUrl;
|
||||
|
||||
// // 添加跨域属性(如果需要)
|
||||
// video.setAttribute('crossOrigin', 'anonymous');
|
||||
|
||||
// // 开始加载元数据
|
||||
// video.load();
|
||||
// });
|
||||
// };
|
||||
Loading…
x
Reference in New Issue
Block a user