January 1, 2026
2 min read
By devshan

Table of Contents

This is a list of all the sections in this post. Click on any of them to jump to that section.

背景

由于生成缩略图需要耗费很多流量(图片需要下载原图生成,视频需要流媒体提取帧),不再默认自动重生成丢失的缩略图,改为可控的策略。

实现内容

1. 设置页面选项

在设置页面添加两个独立开关:

  • 自动生成图片缩略图:缩略图丢失时自动重新生成,会耗费流量
  • 自动生成视频缩略图:缩略图丢失时自动重新生成,会耗费流量

配置存储在 SharedPreferences,key 为:

  • auto_gen_missing_image_thumbs
  • auto_gen_missing_video_thumbs

2. 浏览时的行为

修改 fetchOrGenerateThumbnail 函数,增加 autoGenImageautoGenVideo 参数:

  • 先查本地缓存
  • 尝试从后端获取
  • 如果不存在,根据配置决定是否自动重新生成

3. 文件菜单生成缩略图选项

在文件操作菜单中添加”生成缩略图”选项:

单文件(图片/视频)

  • 点击后弹窗提示会耗费流量
  • 确认后强制重新生成(先删除旧的再生成)

文件夹

  • 两个勾选项:
    • 递归包含子文件夹
    • 仅生成缺失的缩略图
  • 勾选后动态更新统计数量
  • 分别显示图片和视频数量
  • 两个按钮:生成图片、生成视频

4. 删除提示

在缩略图文件夹(/.thumbs/)下删除文件时,弹窗增加流量警告: “删除后再次生成缩略图会耗费网络/S3传输流量”

技术细节

图片缩略图生成流程

  1. 调用 deleteThumbnail API 删除后端缩略图
  2. 清除本地缓存
  3. 调用 getThumbnailWithInfo API,后端自动下载原图并生成缩略图
  4. 保存到本地缓存

视频缩略图生成流程

  1. 获取流媒体 URL
  2. 前端调用 VideoThumbnailService 提取帧
    • Android: MediaMetadataRetriever
    • Desktop: FFmpeg
  3. 调用 uploadThumbnail API 上传
  4. 保存到本地缓存

统计文件夹内媒体文件

Future<({int imageCount, int videoCount, List<FileMetadata> files})>
    _countMediaFiles({
  required String folderPath,
  required bool recursive,
  required bool onlyMissing,
}) async {
  // 递归:f.path.startsWith(folderPath)
  // 非递归:f.path == folderPath
  // 仅缺失:检查本地缓存是否存在
}

修改的文件

  • lib/core/state/app_state.dart - 配置变量和持久化
  • lib/ui/settings_page.dart - 设置开关 UI
  • lib/ui/widgets/file_tiles.dart - fetchOrGenerateThumbnail 函数
  • lib/ui/chat_page.dart - 调用点更新
  • lib/ui/widgets/file_action_sheet.dart - onGenerateThumbnail 回调
  • lib/core/services/file_operation_service.dart - 完整实现

关键逻辑修复

问题1:图片缩略图不会强制重新生成

后端 downloadThumbnail 如果缩略图已存在会直接返回,不会重新生成。 修复:先调用 deleteThumbnail 再获取。

问题2:视频缩略图生成失败计数不准

生成失败但不抛异常时,successCount 仍然增加。 修复:生成失败时抛出异常。

问题3:文件夹只处理直接子文件

修复:添加”递归包含子文件夹”勾选项。

问题4:无法跳过已有缩略图

修复:添加”仅生成缺失的缩略图”勾选项,勾选时检查本地缓存。