MKV 묶음 취소: 지금 것만 / 남은 것 모두 / 그대로 · 취소됨 상태 (186 · 187)
- 186 (사용자 편의, 기기 확인 지적): 묶음 실행 중 취소를 누르면 기다리는 항목이 있을 때 세 가지 중 고르기 - 187 (버그): 취소한 항목이 실패로 보이던 것 - JobStatus.cancelled, 회색으로 보이고 기록에 성공 · 취소 · 실패 / 전체 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>fix_mkv
parent
c9c4f031b5
commit
27d50891d0
|
|
@ -570,6 +570,8 @@
|
|||
"날짜": "Date",
|
||||
"남는 것": "What stays",
|
||||
"남은 {0}장": "{0} left",
|
||||
"남은 것 모두 취소 ({0}개)": "Cancel all remaining ({0})",
|
||||
"남은 것: MKV 만들기 {0}개{1}": "Remaining: {0} MKV{1}",
|
||||
"남은 공간 {0}": "{0} free",
|
||||
"남은 시간 {0}": "{0} left",
|
||||
"남은 약 {0}": "about {0} left",
|
||||
|
|
@ -1328,6 +1330,7 @@
|
|||
"완료": "Done",
|
||||
"완료 정리": "Clear completed",
|
||||
"완료: 성공 {0} / 전체 {1}": "Done: {0} succeeded / {1} total",
|
||||
"완료: 성공 {0} · 취소 {1} · 실패 {2} / 전체 {3}": "Done: {0} succeeded · {1} cancelled · {2} failed / {3} total",
|
||||
"완료시 자동 동영상추가": "Auto-add videos when done",
|
||||
"외부": "External",
|
||||
"외부 브라우저": "External browser",
|
||||
|
|
@ -1745,6 +1748,8 @@
|
|||
"즐겨찾기에서 빼기 (폴더는 그대로)": "Remove from favorites (the folder stays)",
|
||||
"지금": "Current",
|
||||
"지금 {0}%": "Now {0}%",
|
||||
"지금 것만 취소 (다음 것부터 이어서)": "Cancel only the current one (continue with the next)",
|
||||
"지금 것만 취소: {0}": "Cancelled only the current one: {0}",
|
||||
"지금 도는 복사는 앞의 옵션으로 돌고 있습니다. 멈추고 새 옵션으로 다시 실행할까요?": "The running copy uses the previous options. Stop it and run again with the new options?",
|
||||
"지금 마스터 (또는 최상) 비밀번호": "Current master (or top) password",
|
||||
"지금 맞추기": "Sync now",
|
||||
|
|
@ -1855,6 +1860,7 @@
|
|||
"취소": "Cancel",
|
||||
"취소 · 실패한 다운로드가 남긴 .part · .ytdl · .aria2 · 합치기 전 조각 파일 (jj_yt-dlp · jj_aria2)": ".part · .ytdl · .aria2 · unmerged fragments left by cancelled · failed downloads (jj_yt-dlp · jj_aria2)",
|
||||
"취소됨": "Cancelled",
|
||||
"취소됨: {0}": "Cancelled: {0}",
|
||||
"취소했습니다": "Cancelled",
|
||||
"취소했습니다.": "Cancelled.",
|
||||
"칠한 시간에만 동기화하고, 그 밖의 시간에는 바뀐 것을 모아 두었다가 다음 시작 시간에 맞춥니다. 칸을 누르거나 끌어서 칠합니다 (요일 이름 · 시 숫자를 누르면 그 줄 전체).": "Syncs only in the painted hours; changes outside are collected and synced at the next start. Tap or drag cells to paint (tap a weekday name · hour number for the whole row · column).",
|
||||
|
|
|
|||
|
|
@ -570,6 +570,8 @@
|
|||
"날짜": "日付",
|
||||
"남는 것": "残るもの",
|
||||
"남은 {0}장": "残り {0} 枚",
|
||||
"남은 것 모두 취소 ({0}개)": "残りをすべて取り消し ({0} 件)",
|
||||
"남은 것: MKV 만들기 {0}개{1}": "残り: MKV 作成 {0} 件{1}",
|
||||
"남은 공간 {0}": "空き容量 {0}",
|
||||
"남은 시간 {0}": "残り時間 {0}",
|
||||
"남은 약 {0}": "残り約 {0}",
|
||||
|
|
@ -1328,6 +1330,7 @@
|
|||
"완료": "完了",
|
||||
"완료 정리": "完了を整理",
|
||||
"완료: 성공 {0} / 전체 {1}": "完了: 成功 {0} / 全体 {1}",
|
||||
"완료: 성공 {0} · 취소 {1} · 실패 {2} / 전체 {3}": "完了: 成功 {0} · 取消 {1} · 失敗 {2} / 全 {3}",
|
||||
"완료시 자동 동영상추가": "完了時に動画を自動追加",
|
||||
"외부": "外部",
|
||||
"외부 브라우저": "外部ブラウザー",
|
||||
|
|
@ -1745,6 +1748,8 @@
|
|||
"즐겨찾기에서 빼기 (폴더는 그대로)": "お気に入りから外す (フォルダーはそのまま)",
|
||||
"지금": "現在",
|
||||
"지금 {0}%": "現在 {0}%",
|
||||
"지금 것만 취소 (다음 것부터 이어서)": "今のものだけ取り消し (次から続ける)",
|
||||
"지금 것만 취소: {0}": "今のものだけ取り消し: {0}",
|
||||
"지금 도는 복사는 앞의 옵션으로 돌고 있습니다. 멈추고 새 옵션으로 다시 실행할까요?": "実行中のコピーは前のオプションで動いています。止めて新しいオプションで再実行しますか?",
|
||||
"지금 마스터 (또는 최상) 비밀번호": "現在のマスター (または最上位) パスワード",
|
||||
"지금 맞추기": "今すぐ同期",
|
||||
|
|
@ -1855,6 +1860,7 @@
|
|||
"취소": "キャンセル",
|
||||
"취소 · 실패한 다운로드가 남긴 .part · .ytdl · .aria2 · 합치기 전 조각 파일 (jj_yt-dlp · jj_aria2)": "キャンセル・失敗したダウンロードが残した .part・.ytdl・.aria2・結合前の断片(jj_yt-dlp・jj_aria2)",
|
||||
"취소됨": "キャンセル済み",
|
||||
"취소됨: {0}": "取り消し: {0}",
|
||||
"취소했습니다": "キャンセルしました",
|
||||
"취소했습니다.": "キャンセルしました。",
|
||||
"칠한 시간에만 동기화하고, 그 밖의 시간에는 바뀐 것을 모아 두었다가 다음 시작 시간에 맞춥니다. 칸을 누르거나 끌어서 칠합니다 (요일 이름 · 시 숫자를 누르면 그 줄 전체).": "塗った時間だけ同期し、それ以外は変更を貯めて次の開始時刻に同期します。マスをタップまたはドラッグして塗ります (曜日名 · 時の数字をタップするとその行 · 列全体)。",
|
||||
|
|
|
|||
|
|
@ -570,6 +570,8 @@
|
|||
"날짜": "日期",
|
||||
"남는 것": "保留的内容",
|
||||
"남은 {0}장": "剩余 {0} 张",
|
||||
"남은 것 모두 취소 ({0}개)": "取消全部剩余项({0} 个)",
|
||||
"남은 것: MKV 만들기 {0}개{1}": "剩余:MKV 制作 {0} 个{1}",
|
||||
"남은 공간 {0}": "剩余空间 {0}",
|
||||
"남은 시간 {0}": "剩余时间 {0}",
|
||||
"남은 약 {0}": "剩余约 {0}",
|
||||
|
|
@ -1328,6 +1330,7 @@
|
|||
"완료": "完成",
|
||||
"완료 정리": "清理已完成",
|
||||
"완료: 성공 {0} / 전체 {1}": "完成: 成功 {0} / 共 {1}",
|
||||
"완료: 성공 {0} · 취소 {1} · 실패 {2} / 전체 {3}": "完成:成功 {0} · 取消 {1} · 失败 {2} / 共 {3}",
|
||||
"완료시 자동 동영상추가": "完成后自动添加视频",
|
||||
"외부": "外部",
|
||||
"외부 브라우저": "外部浏览器",
|
||||
|
|
@ -1745,6 +1748,8 @@
|
|||
"즐겨찾기에서 빼기 (폴더는 그대로)": "从收藏中移除(文件夹保留)",
|
||||
"지금": "当前",
|
||||
"지금 {0}%": "当前 {0}%",
|
||||
"지금 것만 취소 (다음 것부터 이어서)": "仅取消当前项(从下一个继续)",
|
||||
"지금 것만 취소: {0}": "仅取消当前项:{0}",
|
||||
"지금 도는 복사는 앞의 옵션으로 돌고 있습니다. 멈추고 새 옵션으로 다시 실행할까요?": "正在运行的复制使用的是之前的选项。停止并用新选项重新运行吗?",
|
||||
"지금 마스터 (또는 최상) 비밀번호": "当前主密码 (或最高密码)",
|
||||
"지금 맞추기": "立即同步",
|
||||
|
|
@ -1855,6 +1860,7 @@
|
|||
"취소": "取消",
|
||||
"취소 · 실패한 다운로드가 남긴 .part · .ytdl · .aria2 · 합치기 전 조각 파일 (jj_yt-dlp · jj_aria2)": "取消 · 失败的下载留下的 .part · .ytdl · .aria2 · 合并前的碎片文件(jj_yt-dlp · jj_aria2)",
|
||||
"취소됨": "已取消",
|
||||
"취소됨: {0}": "已取消:{0}",
|
||||
"취소했습니다": "已取消",
|
||||
"취소했습니다.": "已取消。",
|
||||
"칠한 시간에만 동기화하고, 그 밖의 시간에는 바뀐 것을 모아 두었다가 다음 시작 시간에 맞춥니다. 칸을 누르거나 끌어서 칠합니다 (요일 이름 · 시 숫자를 누르면 그 줄 전체).": "只在涂色的时间同步,其他时间的变化累积到下次开始再同步。点按或拖动格子涂色 (点按星期名 · 小时数字可选整行 · 整列)。",
|
||||
|
|
|
|||
|
|
@ -1328,25 +1328,51 @@ class AppController extends ChangeNotifier {
|
|||
if (queue.isEmpty) return;
|
||||
final limit = settings.maxParallelJobs <= 0 ? queue.length : settings.maxParallelJobs;
|
||||
if (queue.length > 1) _log(trf('MKV 만들기: {0}개, 동시에 {1}개씩', [queue.length, limit.clamp(1, queue.length)]));
|
||||
var next = 0;
|
||||
_batchQueue = queue;
|
||||
_batchNext = 0;
|
||||
Future<void> worker() async {
|
||||
while (!_buildCancelled && next < queue.length) {
|
||||
await _build(queue[next++]);
|
||||
while (!_buildCancelled && _batchNext < queue.length) {
|
||||
await _build(queue[_batchNext++]);
|
||||
}
|
||||
}
|
||||
|
||||
await Future.wait([for (var i = 0; i < limit.clamp(1, queue.length); i++) worker()]);
|
||||
// 이번에 만든 것 기준 (목록의 다른 동영상은 세지 않음)
|
||||
final ok = queue.where((v) => v.status == JobStatus.done).length;
|
||||
_log(trf('완료: 성공 {0} / 전체 {1}', [ok, queue.length]));
|
||||
// 이번에 만든 것 기준 (목록의 다른 동영상은 세지 않음). 187: 취소는 실패로 세지 않는다
|
||||
int count(JobStatus s) => queue.where((v) => v.status == s).length;
|
||||
_log(trf('완료: 성공 {0} · 취소 {1} · 실패 {2} / 전체 {3}',
|
||||
[count(JobStatus.done), count(JobStatus.cancelled), count(JobStatus.failed), queue.length]));
|
||||
} finally {
|
||||
buildingMkv = false;
|
||||
_batchQueue = const [];
|
||||
_batchNext = 0;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// 186: 지금 MKV 만들기 묶음 (차례를 기다리는 동영상도 "대기" 로 센다)
|
||||
List<VideoItem> _batchQueue = const [];
|
||||
int _batchNext = 0;
|
||||
|
||||
/// 186: 지금 묶음에서 아직 시작하지 않은 동영상 수
|
||||
int get batchWaiting => buildingMkv ? (_batchQueue.length - _batchNext).clamp(0, _batchQueue.length) : 0;
|
||||
|
||||
/// 186: 지금 만드는 동영상 이름 (취소 창에 보일 것)
|
||||
List<String> get runningVideoNames => [for (final v in videos) if (v.status == JobStatus.running) v.fileName];
|
||||
|
||||
/// 186 · 187: 취소 요청 차례 - 만들던 중에 바뀌었으면 그 동영상은 "취소됨"
|
||||
int _cancelGen = 0;
|
||||
|
||||
/// 186: 지금 만드는 동영상만 취소하고 묶음의 다음 것부터 이어서 (대기열의 다른 작업도 그대로)
|
||||
void cancelRunningVideos() {
|
||||
if (!buildingMkv) return cancelCurrent();
|
||||
_log(trf('지금 것만 취소: {0}', [runningVideoNames.join(', ')]));
|
||||
_cancelGen++;
|
||||
_tool.cancel();
|
||||
}
|
||||
|
||||
Future<void> _build(VideoItem v) async {
|
||||
final out = outputMkvPath(v.path);
|
||||
final gen = _cancelGen;
|
||||
v
|
||||
..status = JobStatus.running
|
||||
..progress = 0
|
||||
|
|
@ -1386,10 +1412,12 @@ class AppController extends ChangeNotifier {
|
|||
: e is ArgumentError
|
||||
? '${e.message}'
|
||||
: '$e';
|
||||
// 187: 사용자가 취소한 것은 실패가 아니라 "취소됨"
|
||||
final cancelled = gen != _cancelGen || _buildCancelled;
|
||||
v
|
||||
..status = JobStatus.failed
|
||||
..message = msg;
|
||||
_log(trf('실패: {0}\n{1}', [v.fileName, msg]));
|
||||
..status = cancelled ? JobStatus.cancelled : JobStatus.failed
|
||||
..message = cancelled ? tr('취소됨') : msg;
|
||||
_log(cancelled ? trf('취소됨: {0}', [v.fileName]) : trf('실패: {0}\n{1}', [v.fileName, msg]));
|
||||
// 만들다 만 파일은 지운다 (0 바이트 MKV 가 남지 않도록). 시작 전에 실패했으면 전에 만든 MKV 는 그대로 둔다.
|
||||
if (started) {
|
||||
try {
|
||||
|
|
@ -1418,6 +1446,7 @@ class AppController extends ChangeNotifier {
|
|||
/// 45: 지금 작업만 멈추고 대기 중인 작업은 이어서 (다음 작업이 시작하면 취소 표시는 다시 꺼진다)
|
||||
void cancelCurrent() {
|
||||
if (!busy) return;
|
||||
_cancelGen++;
|
||||
_log(trf('지금 작업 취소: {0}', [currentJob ?? '']));
|
||||
_recognizer?.cancel();
|
||||
_buildCancelled = true;
|
||||
|
|
@ -1427,6 +1456,7 @@ class AppController extends ChangeNotifier {
|
|||
}
|
||||
|
||||
void cancel() {
|
||||
_cancelGen++;
|
||||
_recognizer?.cancel();
|
||||
if (pendingJobs.isNotEmpty) _log(trf('대기 중인 작업 {0}개 취소', [pendingJobs.length]));
|
||||
pendingJobs.clear();
|
||||
|
|
|
|||
|
|
@ -84,7 +84,8 @@ class SubtitleEntry {
|
|||
: trf('트랙 #{0} ({1}){2}', [streamIndex, codec, title != null ? ' - $title' : '']);
|
||||
}
|
||||
|
||||
enum JobStatus { ready, running, done, failed }
|
||||
/// [cancelled] (187): 사용자가 취소함 - 실패가 아니다 (회색 "취소됨", 작업 기록에서 실패로 세지 않음)
|
||||
enum JobStatus { ready, running, done, failed, cancelled }
|
||||
|
||||
class VideoItem {
|
||||
final String path;
|
||||
|
|
|
|||
|
|
@ -135,12 +135,44 @@ class _TopBar extends StatelessWidget {
|
|||
/// 고른 동영상이 없을 때: 목록 전체를 만들지 묻는다
|
||||
/// 보고 있는 동영상의 결과 폴더 (jj_mkv) 를 탐색기 · 파일 앱으로. 아직 없으면 알린다.
|
||||
/// 45: 대기 중인 작업이 있으면 지금 작업만 / 모두 고르게 한다 (없으면 지금 작업을 바로 멈춤 - 예전처럼)
|
||||
/// 186: 한 묶음 안의 차례를 기다리는 동영상 (MKV 만들기 3개 중 v2 · v3) 도 대기로 센다 - 남은 것이 있으면
|
||||
/// "지금 것만 취소 · 남은 것 모두 취소 · 그대로 두기" 를 묻는다 (예전에는 묻지 않고 모두 멈췄다)
|
||||
Future<void> _cancelJobs(BuildContext context) async {
|
||||
final waiting = c.pendingJobs.length;
|
||||
final inBatch = c.batchWaiting;
|
||||
final waiting = c.pendingJobs.length + inBatch;
|
||||
if (waiting == 0) {
|
||||
c.cancel();
|
||||
return;
|
||||
}
|
||||
if (inBatch > 0) {
|
||||
final now = c.runningVideoNames.join(', ');
|
||||
final pick = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
scrollable: true,
|
||||
actionsOverflowDirection: VerticalDirection.down, // 110: [그대로 두기] 가 먼저
|
||||
title: Text(tr('작업 취소')),
|
||||
content: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Text(trf('지금: {0}', [now.isEmpty ? (c.currentJob ?? '') : now])),
|
||||
const SizedBox(height: 4),
|
||||
Text(trf('남은 것: MKV 만들기 {0}개{1}', [inBatch, c.pendingJobs.isEmpty ? '' : ' · ${c.pendingJobs.join(' · ')}']),
|
||||
style: const TextStyle(fontSize: 12, color: JjColors.textDim)),
|
||||
]),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(ctx), child: Text(tr('그대로 두기'))),
|
||||
TextButton(onPressed: () => Navigator.pop(ctx, 'current'), child: Text(tr('지금 것만 취소 (다음 것부터 이어서)'))),
|
||||
FilledButton(
|
||||
style: FilledButton.styleFrom(backgroundColor: JjColors.danger),
|
||||
onPressed: () => Navigator.pop(ctx, 'all'),
|
||||
child: Text(trf('남은 것 모두 취소 ({0}개)', [waiting])),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (pick == 'current') c.cancelRunningVideos();
|
||||
if (pick == 'all') c.cancel();
|
||||
return;
|
||||
}
|
||||
final pick = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
|
|
@ -798,7 +830,11 @@ class _VideoTile extends StatelessWidget {
|
|||
Text(v.phase!, style: const TextStyle(fontSize: 11, color: JjColors.textDim))
|
||||
else
|
||||
Text(
|
||||
v.status == JobStatus.failed ? tr('실패') : trf('자막 {0}개', [subs]),
|
||||
v.status == JobStatus.failed
|
||||
? tr('실패')
|
||||
: v.status == JobStatus.cancelled
|
||||
? tr('취소됨')
|
||||
: trf('자막 {0}개', [subs]),
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: v.status == JobStatus.failed
|
||||
|
|
@ -808,7 +844,7 @@ class _VideoTile extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
if (v.status == JobStatus.done || v.status == JobStatus.failed)
|
||||
if (v.status == JobStatus.done || v.status == JobStatus.failed || v.status == JobStatus.cancelled)
|
||||
IconButton(
|
||||
tooltip: tr('다시 만들기 대기로'),
|
||||
iconSize: 16,
|
||||
|
|
@ -842,6 +878,9 @@ class _StatusIcon extends StatelessWidget {
|
|||
const Icon(Icons.check_circle, size: 20, color: JjColors.success),
|
||||
JobStatus.failed =>
|
||||
const Icon(Icons.error, size: 20, color: JjColors.danger),
|
||||
// 187: 취소는 실패가 아니라 회색
|
||||
JobStatus.cancelled =>
|
||||
const Icon(Icons.cancel_outlined, size: 20, color: JjColors.textDim),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ class _VideoRow extends StatelessWidget {
|
|||
JobStatus.running => (Icons.autorenew, JjColors.accent),
|
||||
JobStatus.done => (Icons.check_circle, JjColors.success),
|
||||
JobStatus.failed => (Icons.error, JjColors.danger),
|
||||
JobStatus.cancelled => (Icons.cancel_outlined, JjColors.textDim), // 187
|
||||
};
|
||||
final sub = v.status == JobStatus.running
|
||||
? '${v.phase ?? ''} ${(v.progress * 100).round()}%'
|
||||
|
|
@ -268,6 +269,7 @@ class _VideoRow extends StatelessWidget {
|
|||
switch (v.status) {
|
||||
JobStatus.done => tr('MKV 완성'),
|
||||
JobStatus.failed => trf('실패: {0}', [v.message ?? '']),
|
||||
JobStatus.cancelled => tr('취소됨'),
|
||||
_ => trf('자막 {0}개', [v.subtitles.where((s) => s.enabled).length]),
|
||||
};
|
||||
return Padding(
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:jj_mkvmaker/app/bookmarks_controller.dart';
|
|||
import 'package:jj_mkvmaker/app/download_manager.dart';
|
||||
import 'package:jj_mkvmaker/app/settings.dart';
|
||||
import 'package:jj_mkvmaker/core/download_detect.dart';
|
||||
import 'package:jj_mkvmaker/core/encode_options.dart';
|
||||
import 'package:jj_mkvmaker/core/models.dart';
|
||||
import 'package:jj_mkvmaker/core/output_paths.dart';
|
||||
import 'package:jj_mkvmaker/core/srt.dart';
|
||||
|
|
@ -188,6 +189,39 @@ void main() {
|
|||
expect(one.status, JobStatus.done);
|
||||
});
|
||||
|
||||
test('186 · 187: MKV 3개 중 v1 을 만드는 동안 "지금 것만 취소" - v1 은 취소됨 (실패 아님), v2 · v3 는 이어서 완성', () async {
|
||||
final tool = ProcessMediaTool('ffmpeg', 'ffprobe');
|
||||
if (await tool.version() == null) return markTestSkipped('FFmpeg 없음');
|
||||
final dir = Directory.systemTemp.createTempSync('jj_batch186_');
|
||||
addTearDown(() => dir.deleteSync(recursive: true));
|
||||
// v1 은 다시 인코딩에 시간이 걸리게 길게, v2 · v3 는 짧게
|
||||
final files = [for (final n in ['v1', 'v2', 'v3']) p.join(dir.path, '$n.mp4')];
|
||||
for (final (i, f) in files.indexed) {
|
||||
await tool.runFfmpeg(['-y', '-f', 'lavfi', '-i', 'testsrc=size=1280x720:rate=30', '-t', i == 0 ? '60' : '1',
|
||||
'-c:v', 'libx264', '-preset', 'ultrafast', '-pix_fmt', 'yuv420p', f]);
|
||||
}
|
||||
final c = AppController(PlatformServices(mediaTool: tool, storage: DesktopStorageService()));
|
||||
await c.init();
|
||||
c.settings.maxParallelJobs = 1;
|
||||
c.encode = const EncodeSettings(codec: VideoCodecChoice.h264, quality: QualityChoice.high);
|
||||
await c.addVideos(files);
|
||||
final [v1, v2, v3] = c.videos;
|
||||
final building = c.buildVideos([v1, v2, v3]);
|
||||
// v1 이 만들어지는 중 · 묶음에 2개가 기다림
|
||||
for (var i = 0; i < 400 && !(v1.status == JobStatus.running && v1.progress > 0.02); i++) {
|
||||
await Future<void>.delayed(const Duration(milliseconds: 25));
|
||||
}
|
||||
expect(v1.status, JobStatus.running);
|
||||
expect(c.batchWaiting, 2, reason: '한 묶음 안의 기다리는 동영상도 대기로 센다');
|
||||
c.cancelRunningVideos();
|
||||
await building;
|
||||
expect([v1.status, v2.status, v3.status], [JobStatus.cancelled, JobStatus.done, JobStatus.done],
|
||||
reason: '${v1.message} ${v2.message} ${v3.message}');
|
||||
expect(v1.message, '취소됨');
|
||||
expect(File(outputMkvPath(v1.path)).existsSync(), isFalse, reason: '만들다 만 파일은 지움');
|
||||
expect(c.logs.join('\n'), contains('성공 2 · 취소 1 · 실패 0 / 전체 3'));
|
||||
});
|
||||
|
||||
testWidgets('MKV 화면: 체크 → "자막 만들기 (2)" · "MKV 만들기 (2)" 글, 전체 선택', (tester) async {
|
||||
tester.view.physicalSize = const Size(1600, 900); // 위쪽 버튼 글이 다 보이는 너비
|
||||
tester.view.devicePixelRatio = 1;
|
||||
|
|
|
|||
Loading…
Reference in New Issue