From a94009524c8e3d43b5e855ccc60f4e4b5753e4cc Mon Sep 17 00:00:00 2001 From: RyuJongJin <266630912+RyuJongJin@users.noreply.github.com> Date: Sun, 11 Oct 2026 21:05:27 +0900 Subject: [PATCH] =?UTF-8?q?Windows=20=EA=B2=BD=EB=A1=9C=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EC=9D=98=20smb://=20=C2=B7=20cifs://=20=EB=A5=BC=20\?= =?UTF-8?q?=EC=84=9C=EB=B2=84\=EA=B3=B5=EC=9C=A0=20=EB=A1=9C=20(190)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 관리자 지시 (179 보충): 주소가 틀린 줄 알지 않게, Windows 는 smb://서버/공유 (cifs:// 도) 를 \서버\공유 로 바꿔 연다. %20 같은 주소 글자는 풀고 한글은 그대로. 바꿔도 못 열면 "Windows 의 네트워크 공유는 \서버\공유 형식 (바꾼 경로)" 를 알린다. Co-Authored-By: Claude Opus 5.5 --- assets/l10n/en.json | 1 + assets/l10n/ja.json | 1 + assets/l10n/zh-Hans.json | 1 + lib/ui/explorer_page.dart | 28 ++++++++++++++++++++++++++++ test/explorer_conflict_test.dart | 13 +++++++++++++ 5 files changed, 44 insertions(+) diff --git a/assets/l10n/en.json b/assets/l10n/en.json index df35790..94d6df8 100644 --- a/assets/l10n/en.json +++ b/assets/l10n/en.json @@ -1936,6 +1936,7 @@ "폴더가 들어 있을 때": "When folders are included", "폴더가 아닙니다: {0}": "Not a folder: {0}", "폴더가 없거나 열 수 없습니다": "The folder does not exist or cannot be opened", + "폴더가 없거나 열 수 없습니다. Windows 의 네트워크 공유는 \\\\서버\\공유 형식입니다 (바꾼 경로: {0})": "The folder does not exist or cannot be opened. On Windows, network shares use the \\\\server\\share form (converted path: {0})", "폴더가 없거나 열 수 없습니다: {0}": "The folder doesn't exist or can't be opened: {0}", "폴더가 없습니다": "The folder does not exist", "폴더는 보여도 그 안의 동영상 · 파일은 이 권한이 있어야 보입니다. 허용 화면에서 JJ_MKVMaker 를 켜고 돌아오세요.": "Folders are shown, but the videos and files inside need this permission. Turn on JJ_MKVMaker on the permission screen and come back.", diff --git a/assets/l10n/ja.json b/assets/l10n/ja.json index fb0382a..1d96b58 100644 --- a/assets/l10n/ja.json +++ b/assets/l10n/ja.json @@ -1936,6 +1936,7 @@ "폴더가 들어 있을 때": "フォルダーを含むとき", "폴더가 아닙니다: {0}": "フォルダーではありません: {0}", "폴더가 없거나 열 수 없습니다": "フォルダーがないか開けません", + "폴더가 없거나 열 수 없습니다. Windows 의 네트워크 공유는 \\\\서버\\공유 형식입니다 (바꾼 경로: {0})": "フォルダーが存在しないか開けません。Windows のネットワーク共有は \\\\サーバー\\共有 の形式です (変換したパス: {0})", "폴더가 없거나 열 수 없습니다: {0}": "フォルダーがないか、開けません: {0}", "폴더가 없습니다": "フォルダーがありません", "폴더는 보여도 그 안의 동영상 · 파일은 이 권한이 있어야 보입니다. 허용 화면에서 JJ_MKVMaker 를 켜고 돌아오세요.": "フォルダーは見えても、中の動画・ファイルはこの権限が必要です。許可画面で JJ_MKVMaker をオンにして戻ってください。", diff --git a/assets/l10n/zh-Hans.json b/assets/l10n/zh-Hans.json index 705bc34..984e2e3 100644 --- a/assets/l10n/zh-Hans.json +++ b/assets/l10n/zh-Hans.json @@ -1936,6 +1936,7 @@ "폴더가 들어 있을 때": "包含文件夹时", "폴더가 아닙니다: {0}": "不是文件夹: {0}", "폴더가 없거나 열 수 없습니다": "文件夹不存在或无法打开", + "폴더가 없거나 열 수 없습니다. Windows 의 네트워크 공유는 \\\\서버\\공유 형식입니다 (바꾼 경로: {0})": "文件夹不存在或无法打开。Windows 的网络共享格式为 \\\\服务器\\共享(转换后的路径:{0})", "폴더가 없거나 열 수 없습니다: {0}": "文件夹不存在或无法打开: {0}", "폴더가 없습니다": "文件夹不存在", "폴더는 보여도 그 안의 동영상 · 파일은 이 권한이 있어야 보입니다. 허용 화면에서 JJ_MKVMaker 를 켜고 돌아오세요.": "虽然能看到文件夹,但其中的视频和文件需要此权限。请在权限界面开启 JJ_MKVMaker 后返回。", diff --git a/lib/ui/explorer_page.dart b/lib/ui/explorer_page.dart index c049154..a6c2fd0 100644 --- a/lib/ui/explorer_page.dart +++ b/lib/ui/explorer_page.dart @@ -74,6 +74,10 @@ class ExplorerPage extends StatefulWidget { @visibleForTesting static bool? debugAndroidPaths; + /// 시험: 경로 입력 창이 넣은 경로를 고쳐 쓰고 확인하는 것 (갈 경로, 칸 아래 이유) + @visibleForTesting + static Future<(String?, String?)> checkPathForTest(String input) => _ExplorerPageState.checkEnteredPath(input); + static const routeName = 'explorer'; static const rsyncRouteName = 'rsync'; static int _open = 0, _openRsync = 0; @@ -3216,6 +3220,9 @@ class _ExplorerPageState extends State { if (path.isEmpty) return (null, null); // 179: Android 는 네트워크 공유 (SMB) 를 아직 못 연다 - 폴더가 없다고 하지 않고 WebDAV 로 안내 if ((ExplorerPage.debugAndroidPaths ?? Platform.isAndroid) && looksLikeSmb(path)) return (null, smbNotSupported); + // 190: Windows 는 smb:// · cifs:// 주소를 \\서버\공유 로 바꿔 연다 (주소가 틀린 줄 알지 않게) + final unc = Platform.isWindows ? smbToUnc(path) : null; + if (unc != null) path = unc; if (Platform.isWindows && RegExp(r'^\\\\[^\\/]+[\\/]?$').hasMatch(path)) { return (null, tr('네트워크 공유는 공유 폴더 이름까지 넣으세요 (예: \\\\NAS\\영상).')); } @@ -3226,6 +3233,9 @@ class _ExplorerPageState extends State { } catch (_) { ok = false; } + if (!ok && unc != null) { + return (null, trf('폴더가 없거나 열 수 없습니다. Windows 의 네트워크 공유는 \\\\서버\\공유 형식입니다 (바꾼 경로: {0})', [unc])); + } return ok ? (path, null) : (null, tr('폴더가 없거나 열 수 없습니다')); } @@ -3870,6 +3880,24 @@ class _PatternDialogState extends State<_PatternDialog> { /// 179: SMB 경로 (smb:// · \\서버\공유) bool looksLikeSmb(String path) => RegExp(r'^(smb|cifs)://', caseSensitive: false).hasMatch(path) || path.startsWith(r'\\'); +/// 190: smb://서버/공유/폴더 (cifs:// 도) → \\서버\공유\폴더. SMB 주소가 아니면 null +String? smbToUnc(String path) { + final m = RegExp(r'^(smb|cifs)://(.+)$', caseSensitive: false).firstMatch(path.trim()); + if (m == null) return null; + final parts = m.group(2)!.split(RegExp(r'[/\\]+')).where((s) => s.isNotEmpty).map(_decodeSmbPart).toList(); + if (parts.isEmpty) return null; + return '\\\\${parts.join('\\')}'; +} + +/// %20 같은 주소 글자는 풀고, 한글 등 그대로 쓴 글자는 그대로 +String _decodeSmbPart(String s) { + try { + return Uri.decodeComponent(s); + } catch (_) { + return s; + } +} + String get smbNotSupported => tr('네트워크 공유 (SMB) 는 아직 지원하지 않습니다. WebDAV 탭을 쓰세요'); class _PathDialog extends StatefulWidget { diff --git a/test/explorer_conflict_test.dart b/test/explorer_conflict_test.dart index 77b1b09..369d6f3 100644 --- a/test/explorer_conflict_test.dart +++ b/test/explorer_conflict_test.dart @@ -408,6 +408,19 @@ void main() { expect(find.textContaining(right), findsWidgets); }); + test('190: Windows 경로 입력의 smb:// · cifs:// 는 \\\\서버\\공유 로 바꿔 열고, 못 열면 그 형식을 알린다', () async { + expect(smbToUnc('smb://nas/영상/2024'), r'\\nas\영상\2024'); + expect(smbToUnc('CIFS://nas.local/share/'), r'\\nas.local\share'); + expect(smbToUnc('smb://nas/My%20Videos'), r'\\nas\My Videos'); + expect(smbToUnc(r'\\nas\share'), isNull); + expect(smbToUnc('D:/x'), isNull); + if (!Platform.isWindows) return; + final (path, error) = await ExplorerPage.checkPathForTest('smb://localhost/jj_no_such_share_190'); + expect(path, isNull); + expect(error, contains(r'\\localhost\jj_no_such_share_190')); + expect(error, contains('형식')); + }); + testWidgets('179: Android 에서 SMB 경로를 넣으면 "아직 지원하지 않음 · WebDAV" 안내와 [WebDAV 서버 추가]', (tester) async { ExplorerPage.debugAndroidPaths = true; addTearDown(() => ExplorerPage.debugAndroidPaths = null);