여러 장 AI 작업의 진행 줄: "10장 중 3장째 · 남은 약 …" (161)

폴더 · ZIP 전체 해상도 올리기와 AI 그림 여러 장. Android 진행 알림 · 작업 현황 · AI 그림 화면에 같은 줄.
제목에 붙이던 "3/10" · "남은 n장" 대신 AiJobs.total 로 센다. ai_upscale_test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
묶음4
RyuJongJin 2026-10-10 03:53:20 +09:00
parent afdbcfb2d7
commit ecb1cf93b7
5 changed files with 16 additions and 2 deletions

View File

@ -379,6 +379,7 @@
"{0}장 모두 해상도 올리기": "Upscale all {0} pages", "{0}장 모두 해상도 올리기": "Upscale all {0} pages",
"{0}장 저장: {1}": "{0} saved: {1}", "{0}장 저장: {1}": "{0} saved: {1}",
"{0}장 중 {1}장에서 멈췄습니다": "Stopped after {1} of {0}", "{0}장 중 {1}장에서 멈췄습니다": "Stopped after {1} of {0}",
"{0}장 중 {1}장째": "{1} of {0}",
"{0}장은 실패했습니다": "{0} failed", "{0}장은 실패했습니다": "{0} failed",
"{0}장을 올려 저장했습니다{1}": "Upscaled and saved {0}{1}", "{0}장을 올려 저장했습니다{1}": "Upscaled and saved {0}{1}",
"{0}줄": "{0} lines", "{0}줄": "{0} lines",

View File

@ -379,6 +379,7 @@
"{0}장 모두 해상도 올리기": "{0} ページすべてを高解像度化", "{0}장 모두 해상도 올리기": "{0} ページすべてを高解像度化",
"{0}장 저장: {1}": "{0} 枚保存: {1}", "{0}장 저장: {1}": "{0} 枚保存: {1}",
"{0}장 중 {1}장에서 멈췄습니다": "{0} 枚中 {1} 枚で停止しました", "{0}장 중 {1}장에서 멈췄습니다": "{0} 枚中 {1} 枚で停止しました",
"{0}장 중 {1}장째": "{0} 枚中 {1} 枚目",
"{0}장은 실패했습니다": "{0} ページは失敗しました", "{0}장은 실패했습니다": "{0} ページは失敗しました",
"{0}장을 올려 저장했습니다{1}": "{0} ページを高解像度化して保存しました{1}", "{0}장을 올려 저장했습니다{1}": "{0} ページを高解像度化して保存しました{1}",
"{0}줄": "{0}行", "{0}줄": "{0}行",

View File

@ -379,6 +379,7 @@
"{0}장 모두 해상도 올리기": "提高全部 {0} 页的分辨率", "{0}장 모두 해상도 올리기": "提高全部 {0} 页的分辨率",
"{0}장 저장: {1}": "已保存 {0} 张: {1}", "{0}장 저장: {1}": "已保存 {0} 张: {1}",
"{0}장 중 {1}장에서 멈췄습니다": "共 {0} 张,在第 {1} 张后停止", "{0}장 중 {1}장에서 멈췄습니다": "共 {0} 张,在第 {1} 张后停止",
"{0}장 중 {1}장째": "第 {1} 张,共 {0} 张",
"{0}장은 실패했습니다": "{0} 页失败", "{0}장은 실패했습니다": "{0} 页失败",
"{0}장을 올려 저장했습니다{1}": "已提高并保存 {0} 页{1}", "{0}장을 올려 저장했습니다{1}": "已提高并保存 {0} 页{1}",
"{0}줄": "{0} 行", "{0}줄": "{0} 行",

View File

@ -567,6 +567,7 @@ class AiJobs extends ChangeNotifier {
void start(String t, ImageAiEngine engine, int count) { void start(String t, ImageAiEngine engine, int count) {
title = t; title = t;
_engine = engine; _engine = engine;
total = count;
remaining = count; remaining = count;
progress = 0; progress = 0;
eta = null; eta = null;
@ -590,6 +591,7 @@ class AiJobs extends ChangeNotifier {
inUse = const {}; inUse = const {};
title = null; title = null;
progress = null; progress = null;
total = 0;
remaining = 0; remaining = 0;
eta = null; eta = null;
_engine = null; _engine = null;
@ -611,6 +613,7 @@ class AiJobs extends ChangeNotifier {
lastDoneDir = null; lastDoneDir = null;
var stop = false; var stop = false;
this.title = title; this.title = title;
total = count;
remaining = count; remaining = count;
progress = 0; progress = 0;
eta = null; eta = null;
@ -630,7 +633,6 @@ class AiJobs extends ChangeNotifier {
progress: overall, progress: overall,
remaining: count - i, remaining: count - i,
eta: overall > 0.02 ? spent * ((1 - overall) / overall) : null, eta: overall > 0.02 ? spent * ((1 - overall) / overall) : null,
title: '$title ${i + 1}/$count',
); );
}); });
done++; done++;
@ -682,9 +684,14 @@ class AiJobs extends ChangeNotifier {
final t = title; final t = title;
if (t == null) return null; if (t == null) return null;
final left = eta == null ? '' : ' · ${trf('남은 약 {0}', [_dur(eta!)])}'; final left = eta == null ? '' : ' · ${trf('남은 약 {0}', [_dur(eta!)])}';
return '$t${remaining > 1 ? ' · ${trf('남은 {0}장', [remaining])}' : ''}$left'; // 161: 여러 장이면 "10장 중 3장째" (알림 · 작업 현황 · 그림 화면)
final at = total > 1 ? ' · ${trf('{0}장 중 {1}장째', [total, (total - remaining + 1).clamp(1, total)])}' : '';
return '$t$at$left';
} }
/// 161: 이번 작업의 장 수 (남은 장 [remaining] 과 함께 "n장 중 m장째")
int total = 0;
static String durationText(Duration d) => _dur(d); static String durationText(Duration d) => _dur(d);
static String _dur(Duration d) => d.inMinutes >= 1 ? trf('{0}분', [d.inMinutes + (d.inSeconds % 60 >= 30 ? 1 : 0)]) : trf('{0}초', [d.inSeconds]); static String _dur(Duration d) => d.inMinutes >= 1 ? trf('{0}분', [d.inMinutes + (d.inSeconds % 60 >= 30 ? 1 : 0)]) : trf('{0}초', [d.inSeconds]);

View File

@ -56,14 +56,18 @@ void main() {
test('121: 여러 장 작업 - 진행 · 남은 장 · 취소하면 지금 장에서 멈춤', () async { test('121: 여러 장 작업 - 진행 · 남은 장 · 취소하면 지금 장에서 멈춤', () async {
final jobs = AiJobs.instance; final jobs = AiJobs.instance;
final seen = <int>[]; final seen = <int>[];
final lines = <String?>[];
var cancelled = 0; var cancelled = 0;
final done = await jobs.runTask('올리기', 5, (i, progress) async { final done = await jobs.runTask('올리기', 5, (i, progress) async {
progress(0.5); progress(0.5);
seen.add(jobs.remaining); seen.add(jobs.remaining);
lines.add(jobs.statusLine);
if (i == 2) jobs.cancel(); if (i == 2) jobs.cancel();
}, onCancel: () => cancelled++); }, onCancel: () => cancelled++);
expect(done, 3); expect(done, 3);
expect(seen, [5, 4, 3]); expect(seen, [5, 4, 3]);
// 161: 알림 · 작업 현황에 "5장 중 m장째"
expect(lines.map((l) => l!.split(' · ').take(2).join(' · ')), ['올리기 · 5장 중 1장째', '올리기 · 5장 중 2장째', '올리기 · 5장 중 3장째']);
expect(cancelled, 1); expect(cancelled, 1);
expect(jobs.busy, isFalse); expect(jobs.busy, isFalse);
}); });