전체 점검에서 고침: 다시 켜면 전에 만든 자막 (jj_mkv) 다시 연결, Android 16 태블릿에서 화면 방향 고정 동작, 목록 머리글 한 줄, '이 기기에서' 띄어쓰기
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>main
parent
52deef4118
commit
98122539e7
|
|
@ -17,6 +17,10 @@
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:requestLegacyExternalStorage="true">
|
android:requestLegacyExternalStorage="true">
|
||||||
|
<!-- Android 16: 큰 화면 (태블릿) 에서도 환경 설정 > 화면 방향 (가로 고정 · 세로 고정) 을 따르게 -->
|
||||||
|
<property
|
||||||
|
android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY"
|
||||||
|
android:value="true" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
" · 다시 시작해야 Chrome 으로 바뀝니다": " · Restart to switch to Chrome",
|
" · 다시 시작해야 Chrome 으로 바뀝니다": " · Restart to switch to Chrome",
|
||||||
" · 대기 {0}": " · {0} waiting",
|
" · 대기 {0}": " · {0} waiting",
|
||||||
" · 대기 {0}개": " · {0} waiting",
|
" · 대기 {0}개": " · {0} waiting",
|
||||||
|
" · 전에 만든 자막 {0}개 (jj_mkv)": " · {0} subtitles made earlier (jj_mkv)",
|
||||||
" → MKV 만들기": " → Make MKV",
|
" → MKV 만들기": " → Make MKV",
|
||||||
" ⚠ 사용할 수 없는 코덱: {0}": " ⚠ Unavailable codec: {0}",
|
" ⚠ 사용할 수 없는 코덱: {0}": " ⚠ Unavailable codec: {0}",
|
||||||
" ⚠ 표현할 수 없는 글자 {0}개": " ⚠ {0} characters cannot be represented",
|
" ⚠ 표현할 수 없는 글자 {0}개": " ⚠ {0} characters cannot be represented",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
" · 다시 시작해야 Chrome 으로 바뀝니다": " · 再起動すると Chrome に切り替わります",
|
" · 다시 시작해야 Chrome 으로 바뀝니다": " · 再起動すると Chrome に切り替わります",
|
||||||
" · 대기 {0}": " · 待機 {0}",
|
" · 대기 {0}": " · 待機 {0}",
|
||||||
" · 대기 {0}개": " · 待機 {0}個",
|
" · 대기 {0}개": " · 待機 {0}個",
|
||||||
|
" · 전에 만든 자막 {0}개 (jj_mkv)": " · 以前作成した字幕 {0}個(jj_mkv)",
|
||||||
" → MKV 만들기": " → MKV 作成",
|
" → MKV 만들기": " → MKV 作成",
|
||||||
" ⚠ 사용할 수 없는 코덱: {0}": " ⚠ 使用できないコーデック: {0}",
|
" ⚠ 사용할 수 없는 코덱: {0}": " ⚠ 使用できないコーデック: {0}",
|
||||||
" ⚠ 표현할 수 없는 글자 {0}개": " ⚠ 表現できない文字 {0}個",
|
" ⚠ 표현할 수 없는 글자 {0}개": " ⚠ 表現できない文字 {0}個",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
" · 다시 시작해야 Chrome 으로 바뀝니다": " · 重新启动后切换到 Chrome",
|
" · 다시 시작해야 Chrome 으로 바뀝니다": " · 重新启动后切换到 Chrome",
|
||||||
" · 대기 {0}": " · 等待 {0}",
|
" · 대기 {0}": " · 等待 {0}",
|
||||||
" · 대기 {0}개": " · 等待 {0} 个",
|
" · 대기 {0}개": " · 等待 {0} 个",
|
||||||
|
" · 전에 만든 자막 {0}개 (jj_mkv)": " · 之前制作的字幕 {0} 个(jj_mkv)",
|
||||||
" → MKV 만들기": " → 制作 MKV",
|
" → MKV 만들기": " → 制作 MKV",
|
||||||
" ⚠ 사용할 수 없는 코덱: {0}": " ⚠ 不可用的编解码器: {0}",
|
" ⚠ 사용할 수 없는 코덱: {0}": " ⚠ 不可用的编解码器: {0}",
|
||||||
" ⚠ 표현할 수 없는 글자 {0}개": " ⚠ {0} 个字符无法表示",
|
" ⚠ 표현할 수 없는 글자 {0}개": " ⚠ {0} 个字符无法表示",
|
||||||
|
|
|
||||||
|
|
@ -340,9 +340,26 @@ class AppController extends ChangeNotifier {
|
||||||
for (final d in siblings) {
|
for (final d in siblings) {
|
||||||
await _addExternal(v, d.path, d.language);
|
await _addExternal(v, d.path, d.language);
|
||||||
}
|
}
|
||||||
|
// 전에 만든 자막 (jj_mkv 의 파일명_ko.srt 등: AI 자막 · 번역 · 인터넷 자막) 도 다시 연결
|
||||||
|
// (앱을 다시 켜거나 목록에 다시 넣어도 MKV 에 빠지지 않게). 음성인식 원본 파일명_AI.srt 는 언어별 파일과 같아서 뺀다.
|
||||||
|
var made = 0;
|
||||||
|
final out = outputDirFor(v.path);
|
||||||
|
if (!p.equals(out, v.directory)) {
|
||||||
|
List<String> files = const [];
|
||||||
|
try {
|
||||||
|
files = await services.storage.listFiles(out);
|
||||||
|
} catch (_) {}
|
||||||
|
for (final d in findSiblingSubtitles(v.path, files)) {
|
||||||
|
if (d.isAi || d.language == undetermined) continue;
|
||||||
|
if (v.subtitles.any((s) => s.path != null && p.equals(s.path!, d.path))) continue;
|
||||||
|
await _addExternal(v, d.path, d.language);
|
||||||
|
made++;
|
||||||
|
}
|
||||||
|
}
|
||||||
final embeddedCount =
|
final embeddedCount =
|
||||||
v.subtitles.where((s) => s.kind == SubtitleKind.embedded).length;
|
v.subtitles.where((s) => s.kind == SubtitleKind.embedded).length;
|
||||||
_log(trf('추가: {0} (내장 자막 {1}개, 같은 폴더 자막 {2}개)', [v.fileName, embeddedCount, siblings.length]));
|
_log(trf('추가: {0} (내장 자막 {1}개, 같은 폴더 자막 {2}개)', [v.fileName, embeddedCount, siblings.length]) +
|
||||||
|
(made > 0 ? trf(' · 전에 만든 자막 {0}개 (jj_mkv)', [made]) : ''));
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -454,15 +454,19 @@ class _VideoList extends StatelessWidget {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: c.videos.isEmpty ? null : c.toggleAllChecked,
|
onTap: c.videos.isEmpty ? null : c.toggleAllChecked,
|
||||||
|
// 좁으면 글자를 줄여서 한 줄에 다 보이게
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
c.checked.isEmpty
|
c.checked.isEmpty
|
||||||
? trf('동영상 {0}개 · 전체 선택', [c.videos.length])
|
? trf('동영상 {0}개 · 전체 선택', [c.videos.length])
|
||||||
: trf('선택 {0} / {1}개', [c.checked.length, c.videos.length]),
|
: trf('선택 {0} / {1}개', [c.checked.length, c.videos.length]),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: const TextStyle(color: JjColors.textDim, fontSize: 12)),
|
style: const TextStyle(color: JjColors.textDim, fontSize: 12)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
// 정렬: 같은 것을 다시 누르면 반대 순서
|
// 정렬: 같은 것을 다시 누르면 반대 순서
|
||||||
_sortButton('name', Icons.sort_by_alpha, tr('파일 이름')),
|
_sortButton('name', Icons.sort_by_alpha, tr('파일 이름')),
|
||||||
_sortButton('date', Icons.calendar_month, tr('날짜')),
|
_sortButton('date', Icons.calendar_month, tr('날짜')),
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,20 @@ void main() {
|
||||||
expect(c.selected, busy);
|
expect(c.selected, busy);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('목록에 다시 넣으면 (앱을 다시 켜도) 전에 만든 자막 (jj_mkv 의 파일명_ko.srt 등) 이 다시 붙는다', () async {
|
||||||
|
final c = _controller();
|
||||||
|
final video = make('강의.mp4').path;
|
||||||
|
make(p.join('jj_mkv', '강의_AI.srt'));
|
||||||
|
make(p.join('jj_mkv', '강의_ko.srt'));
|
||||||
|
make(p.join('jj_mkv', '강의_en.srt'));
|
||||||
|
make(p.join('jj_mkv', '다른영상_ko.srt'));
|
||||||
|
make('강의.ja.srt'); // 같은 폴더 자막
|
||||||
|
await c.addVideos([video]);
|
||||||
|
final v = c.videos.single;
|
||||||
|
expect([for (final s in v.subtitles) p.basename(s.path!)]..sort(), ['강의.ja.srt', '강의_en.srt', '강의_ko.srt']);
|
||||||
|
expect(v.subtitles.firstWhere((s) => s.path!.endsWith('_ko.srt')).language.code, 'ko');
|
||||||
|
});
|
||||||
|
|
||||||
test('이동 폴더를 안 정했으면 옮기지 않는다', () async {
|
test('이동 폴더를 안 정했으면 옮기지 않는다', () async {
|
||||||
final c = _controller();
|
final c = _controller();
|
||||||
final a = VideoItem(make('a.mp4').path);
|
final a = VideoItem(make('a.mp4').path);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue