60 lines
1.9 KiB
Dart
60 lines
1.9 KiB
Dart
import 'dart:io';
|
||
|
||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||
|
||
/// 与图片等分开的专用视频磁盘缓存(`flutter_cache_manager`)。
|
||
final CacheManager funymeeVideoCacheManager = CacheManager(
|
||
Config(
|
||
'funymeeVideoCache',
|
||
stalePeriod: const Duration(days: 14),
|
||
maxNrOfCacheObjects: 200,
|
||
),
|
||
);
|
||
|
||
/// 用 **scheme + host + path** 作缓存键,忽略 **query**(GCS 等签名参数每次变化,否则永远 cache miss)。
|
||
/// 下载请求仍使用完整 [url](含 query)。
|
||
String videoCacheKeyForUrl(String url) {
|
||
final raw = url.trim();
|
||
final u = Uri.tryParse(raw);
|
||
if (u == null || !u.hasScheme) return raw;
|
||
if (u.scheme != 'http' && u.scheme != 'https') return raw;
|
||
if (u.host.isEmpty) return raw;
|
||
final path = u.path.isEmpty ? '/' : u.path;
|
||
return '${u.scheme}://${u.host}$path';
|
||
}
|
||
|
||
/// 排除明显非视频的磁盘文件(HTML 错误页、JSON、过小),避免 ExoPlayer
|
||
/// [UnrecognizedInputFormatException] 反复读坏缓存。
|
||
Future<bool> videoCachedFileLooksPlayable(File f) async {
|
||
try {
|
||
final len = await f.length();
|
||
if (len < 512) return false;
|
||
late List<int> head;
|
||
try {
|
||
head = await f.openRead(0, 16).first;
|
||
} catch (_) {
|
||
return false;
|
||
}
|
||
if (head.length < 12) return false;
|
||
// HTML / XML 错误页
|
||
if (head[0] == 0x3C) {
|
||
final s = String.fromCharCodes(head.take(8));
|
||
if (s.startsWith('<!') || s.startsWith('<?') || s.startsWith('<h')) {
|
||
return false;
|
||
}
|
||
}
|
||
if (head[0] == 0x7B) return false; // JSON
|
||
// MP4/MOV: box size + 'ftyp'
|
||
if (head.length >= 8) {
|
||
final tag = String.fromCharCodes(head.sublist(4, 8));
|
||
if (tag == 'ftyp') return true;
|
||
}
|
||
// WebM EBML
|
||
if (head[0] == 0x1a && head[1] == 0x45 && head[2] == 0xdf) return true;
|
||
// 较大二进制:交给解码器(避免误判少见封装)
|
||
return len >= 4096;
|
||
} catch (_) {
|
||
return false;
|
||
}
|
||
}
|