petsHero-AI/lib/features/gallery/video_thumbnail_cache.dart

64 lines
1.7 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:path_provider/path_provider.dart';
import 'package:video_thumbnail/video_thumbnail.dart';
/// 视频封面缓存:优先从本地读取,未命中则生成并缓存
class VideoThumbnailCache {
VideoThumbnailCache._();
static final VideoThumbnailCache _instance = VideoThumbnailCache._();
static VideoThumbnailCache get instance => _instance;
static const int _maxWidth = 400;
static const int _quality = 75;
Future<Uint8List?> getThumbnail(String videoUrl) async {
final key = _cacheKey(videoUrl);
final cacheDir = await _getCacheDir();
final file = File('${cacheDir.path}/$key.jpg');
if (await file.exists()) {
return file.readAsBytes();
}
try {
final path = await VideoThumbnail.thumbnailFile(
video: videoUrl,
thumbnailPath: cacheDir.path,
imageFormat: ImageFormat.JPEG,
maxWidth: _maxWidth,
quality: _quality,
);
if (path != null) {
final cached = File(path);
final bytes = await cached.readAsBytes();
if (cached.path != file.path) {
await file.writeAsBytes(bytes);
cached.deleteSync();
}
return bytes;
}
} catch (_) {}
return null;
}
String _cacheKey(String url) {
final bytes = utf8.encode(url);
final digest = md5.convert(bytes);
return digest.toString();
}
Directory? _cacheDir;
Future<Directory> _getCacheDir() async {
_cacheDir ??= await getTemporaryDirectory();
final dir = Directory('${_cacheDir!.path}/video_thumbnails');
if (!await dir.exists()) {
await dir.create(recursive: true);
}
return dir;
}
}