This commit is contained in:
2026-08-31 18:35:58 +08:00
parent 36dc258224
commit dbe31b94a3
18 changed files with 822 additions and 58 deletions
+36
View File
@@ -81,6 +81,7 @@ class ModelManager extends ChangeNotifier {
final Set<int> _downloadedIds = {};
final Map<int, double> _progress = {};
final Map<int, String> _errors = {};
final Set<int> _cancelRequested = {};
bool _activeLoaded = false;
bool _ready = false;
bool _refreshing = false;
@@ -104,6 +105,12 @@ class ModelManager extends ChangeNotifier {
/// 下载失败原因(失败后可重试)
String? errorOf(int datasetId) => _errors[datasetId];
/// 中断进行中的下载:下一个数据块到达时终止(丢弃 .part),卡片恢复「使用」。
/// 取消不记错误,可再次下载。
void cancelDownload(int datasetId) {
_cancelRequested.add(datasetId);
}
ModelManager._({String? baseUrl, http.Client? client})
: this(baseUrl: baseUrl, client: client);
@@ -209,11 +216,13 @@ class ModelManager extends ChangeNotifier {
// 并发保护:同一数据集已有进行中的下载则直接短路(预置 0 先占位,
// 使 onProgress 首次回调前的双击/refresh 交错也被 containsKey 拦下)
if (_progress.containsKey(item.datasetId)) return false;
_cancelRequested.remove(item.datasetId);
_progress[item.datasetId] = 0;
final dir = await _modelDir(item.datasetId);
final file = File('${dir.path}/model.tflite');
try {
for (var attempt = 0; attempt < 2; attempt++) {
if (_cancelRequested.contains(item.datasetId)) break;
final ok = await _downloadAndVerify(item, dir, file,
onProgress: (r, t) {
_progress[item.datasetId] = t == 0 ? 0 : r / t;
@@ -227,17 +236,39 @@ class ModelManager extends ChangeNotifier {
await setActive(item.datasetId, true);
return true;
}
if (_cancelRequested.contains(item.datasetId)) break;
await file.delete().catchError((_) => file);
await File('${dir.path}/model.tflite.part')
.delete()
.catchError((_) => file);
}
if (_cancelRequested.contains(item.datasetId)) {
// 用户取消:清理残留,不记错误
await file.delete().catchError((_) => file);
await File('${dir.path}/model.tflite.part')
.delete()
.catchError((_) => file);
_progress.remove(item.datasetId);
notifyListeners();
debugPrint('[ModelManager] 下载已取消: ${item.datasetName}');
return false;
}
_progress.remove(item.datasetId);
_errors[item.datasetId] = '下载失败,请重试';
notifyListeners();
debugPrint('[ModelManager] 下载失败: ${item.datasetName} ${item.version}');
return false;
} catch (e) {
if (_cancelRequested.contains(item.datasetId)) {
await file.delete().catchError((_) => file);
await File('${dir.path}/model.tflite.part')
.delete()
.catchError((_) => file);
_progress.remove(item.datasetId);
notifyListeners();
debugPrint('[ModelManager] 下载已取消: ${item.datasetName}');
return false;
}
_progress.remove(item.datasetId);
_errors[item.datasetId] = '下载异常:$e';
notifyListeners();
@@ -262,10 +293,15 @@ class ModelManager extends ChangeNotifier {
}
final total = res.contentLength ?? item.sizeBytes;
await for (final chunk in res.stream) {
if (_cancelRequested.contains(item.datasetId)) break; // 用户取消
sink.add(chunk);
received += chunk.length;
onProgress?.call(received, total);
}
if (_cancelRequested.contains(item.datasetId)) {
await sink.close();
return false;
}
await sink.close();
final bytes = await part.readAsBytes();
final hex = sha256.convert(bytes).toString();