94 lines
3.3 KiB
Dart
94 lines
3.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../app/ai_local.dart' show AiStore;
|
|
import '../app/app_controller.dart';
|
|
import '../l10n/tr.dart';
|
|
import '../services/model_store.dart';
|
|
import 'confirm.dart';
|
|
import 'setting_tile.dart';
|
|
import 'theme.dart';
|
|
|
|
/// 260: 받은 AI 자막 모델 (Whisper · NLLB) - 차지한 크기 · [지우기]. 다시 쓰면 그때 다시 받는다
|
|
class AiSubtitleModels extends StatefulWidget {
|
|
final AppController c;
|
|
const AiSubtitleModels({super.key, required this.c});
|
|
|
|
@override
|
|
State<AiSubtitleModels> createState() => _AiSubtitleModelsState();
|
|
}
|
|
|
|
class _AiSubtitleModelsState extends State<AiSubtitleModels> {
|
|
/// 모델 id → 차지한 바이트 (0 이면 받지 않음)
|
|
Map<String, int>? _used;
|
|
|
|
/// 다 받은 모델 (나머지 중 크기가 있으면 받다 만 조각)
|
|
final _installed = <String>{};
|
|
String? _error;
|
|
|
|
ModelStore get _store => widget.c.services.models;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_refresh();
|
|
}
|
|
|
|
Future<void> _refresh() async {
|
|
final used = {for (final m in [...whisperModels, nllbModel]) m.id: await _store.usedBytes(m)};
|
|
_installed
|
|
..clear()
|
|
..addAll([for (final m in [...whisperModels, nllbModel]) if (await _store.isInstalled(m)) m.id]);
|
|
if (mounted) setState(() => _used = used);
|
|
}
|
|
|
|
Future<void> _remove(ModelSpec m) async {
|
|
final ok = await confirmAction(context,
|
|
title: trf('{0} 을(를) 지울까요?', [m.label]), body: tr('다시 쓰려면 다시 받아야 합니다.'), ok: tr('지우기'));
|
|
if (!ok) return;
|
|
try {
|
|
await _store.remove(m);
|
|
_error = null;
|
|
} catch (e) {
|
|
_error = trf('지우지 못했습니다: {0}', [e]);
|
|
}
|
|
await _refresh();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final used = _used;
|
|
if (used == null) return const SizedBox();
|
|
return ListenableBuilder(
|
|
listenable: widget.c,
|
|
builder: (context, _) {
|
|
final inUse = widget.c.aiModelsInUse;
|
|
return Column(mainAxisSize: MainAxisSize.min, children: [
|
|
for (final m in [...whisperModels, nllbModel])
|
|
SettingTile(
|
|
dense: true,
|
|
leading: Icon(used[m.id]! > 0 ? Icons.check_circle : Icons.cloud_download_outlined,
|
|
color: used[m.id]! > 0 ? JjColors.success : JjColors.textDim, size: 20),
|
|
title: Text(m.label),
|
|
subtitle: Text(used[m.id]! > 0
|
|
? trf(_installed.contains(m.id) ? '받음 · {0}' : '받다 만 조각 · {0}', [AiStore.sizeText(used[m.id]!)])
|
|
: trf('받지 않음 (처음 쓸 때 내려받기 {0})', [m.sizeLabel])),
|
|
trailing: used[m.id]! > 0
|
|
? inUse
|
|
? Tooltip(
|
|
message: tr('AI 자막 · 번역 작업이 끝난 뒤 지울 수 있습니다'),
|
|
child: TextButton(onPressed: null, child: Text(tr('지우기'))),
|
|
)
|
|
: TextButton(onPressed: () => _remove(m), child: Text(tr('지우기')))
|
|
: null,
|
|
),
|
|
if (_error != null)
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(16, 4, 16, 0),
|
|
child: Text(_error!, style: TextStyle(fontSize: 12, color: JjColors.danger)),
|
|
),
|
|
]);
|
|
},
|
|
);
|
|
}
|
|
}
|