Windows 경로 입력의 smb:// · cifs:// 를 \서버\공유 로 (190)

관리자 지시 (179 보충): 주소가 틀린 줄 알지 않게, Windows 는 smb://서버/공유 (cifs:// 도) 를 \서버\공유 로 바꿔 연다.
%20 같은 주소 글자는 풀고 한글은 그대로. 바꿔도 못 열면 "Windows 의 네트워크 공유는 \서버\공유 형식 (바꾼 경로)" 를 알린다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix_mkv
RyuJongJin 2026-10-11 21:05:27 +09:00
parent df508e3d2d
commit a94009524c
5 changed files with 44 additions and 0 deletions

View File

@ -1936,6 +1936,7 @@
"폴더가 들어 있을 때": "When folders are included", "폴더가 들어 있을 때": "When folders are included",
"폴더가 아닙니다: {0}": "Not a folder: {0}", "폴더가 아닙니다: {0}": "Not a folder: {0}",
"폴더가 없거나 열 수 없습니다": "The folder does not exist or cannot be opened", "폴더가 없거나 열 수 없습니다": "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}", "폴더가 없거나 열 수 없습니다: {0}": "The folder doesn't exist or can't be opened: {0}",
"폴더가 없습니다": "The folder does not exist", "폴더가 없습니다": "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.", "폴더는 보여도 그 안의 동영상 · 파일은 이 권한이 있어야 보입니다. 허용 화면에서 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.",

View File

@ -1936,6 +1936,7 @@
"폴더가 들어 있을 때": "フォルダーを含むとき", "폴더가 들어 있을 때": "フォルダーを含むとき",
"폴더가 아닙니다: {0}": "フォルダーではありません: {0}", "폴더가 아닙니다: {0}": "フォルダーではありません: {0}",
"폴더가 없거나 열 수 없습니다": "フォルダーがないか開けません", "폴더가 없거나 열 수 없습니다": "フォルダーがないか開けません",
"폴더가 없거나 열 수 없습니다. Windows 의 네트워크 공유는 \\\\서버\\공유 형식입니다 (바꾼 경로: {0})": "フォルダーが存在しないか開けません。Windows のネットワーク共有は \\\\サーバー\\共有 の形式です (変換したパス: {0})",
"폴더가 없거나 열 수 없습니다: {0}": "フォルダーがないか、開けません: {0}", "폴더가 없거나 열 수 없습니다: {0}": "フォルダーがないか、開けません: {0}",
"폴더가 없습니다": "フォルダーがありません", "폴더가 없습니다": "フォルダーがありません",
"폴더는 보여도 그 안의 동영상 · 파일은 이 권한이 있어야 보입니다. 허용 화면에서 JJ_MKVMaker 를 켜고 돌아오세요.": "フォルダーは見えても、中の動画・ファイルはこの権限が必要です。許可画面で JJ_MKVMaker をオンにして戻ってください。", "폴더는 보여도 그 안의 동영상 · 파일은 이 권한이 있어야 보입니다. 허용 화면에서 JJ_MKVMaker 를 켜고 돌아오세요.": "フォルダーは見えても、中の動画・ファイルはこの権限が必要です。許可画面で JJ_MKVMaker をオンにして戻ってください。",

View File

@ -1936,6 +1936,7 @@
"폴더가 들어 있을 때": "包含文件夹时", "폴더가 들어 있을 때": "包含文件夹时",
"폴더가 아닙니다: {0}": "不是文件夹: {0}", "폴더가 아닙니다: {0}": "不是文件夹: {0}",
"폴더가 없거나 열 수 없습니다": "文件夹不存在或无法打开", "폴더가 없거나 열 수 없습니다": "文件夹不存在或无法打开",
"폴더가 없거나 열 수 없습니다. Windows 의 네트워크 공유는 \\\\서버\\공유 형식입니다 (바꾼 경로: {0})": "文件夹不存在或无法打开。Windows 的网络共享格式为 \\\\服务器\\共享(转换后的路径:{0})",
"폴더가 없거나 열 수 없습니다: {0}": "文件夹不存在或无法打开: {0}", "폴더가 없거나 열 수 없습니다: {0}": "文件夹不存在或无法打开: {0}",
"폴더가 없습니다": "文件夹不存在", "폴더가 없습니다": "文件夹不存在",
"폴더는 보여도 그 안의 동영상 · 파일은 이 권한이 있어야 보입니다. 허용 화면에서 JJ_MKVMaker 를 켜고 돌아오세요.": "虽然能看到文件夹,但其中的视频和文件需要此权限。请在权限界面开启 JJ_MKVMaker 后返回。", "폴더는 보여도 그 안의 동영상 · 파일은 이 권한이 있어야 보입니다. 허용 화면에서 JJ_MKVMaker 를 켜고 돌아오세요.": "虽然能看到文件夹,但其中的视频和文件需要此权限。请在权限界面开启 JJ_MKVMaker 后返回。",

View File

@ -74,6 +74,10 @@ class ExplorerPage extends StatefulWidget {
@visibleForTesting @visibleForTesting
static bool? debugAndroidPaths; static bool? debugAndroidPaths;
/// 시험: 경로 입력 창이 넣은 경로를 고쳐 쓰고 확인하는 것 (갈 경로, 칸 아래 이유)
@visibleForTesting
static Future<(String?, String?)> checkPathForTest(String input) => _ExplorerPageState.checkEnteredPath(input);
static const routeName = 'explorer'; static const routeName = 'explorer';
static const rsyncRouteName = 'rsync'; static const rsyncRouteName = 'rsync';
static int _open = 0, _openRsync = 0; static int _open = 0, _openRsync = 0;
@ -3216,6 +3220,9 @@ class _ExplorerPageState extends State<ExplorerPage> {
if (path.isEmpty) return (null, null); if (path.isEmpty) return (null, null);
// 179: Android 는 네트워크 공유 (SMB) 를 아직 못 연다 - 폴더가 없다고 하지 않고 WebDAV 로 안내 // 179: Android 는 네트워크 공유 (SMB) 를 아직 못 연다 - 폴더가 없다고 하지 않고 WebDAV 로 안내
if ((ExplorerPage.debugAndroidPaths ?? Platform.isAndroid) && looksLikeSmb(path)) return (null, smbNotSupported); 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)) { if (Platform.isWindows && RegExp(r'^\\\\[^\\/]+[\\/]?$').hasMatch(path)) {
return (null, tr('네트워크 공유는 공유 폴더 이름까지 넣으세요 (예: \\\\NAS\\영상).')); return (null, tr('네트워크 공유는 공유 폴더 이름까지 넣으세요 (예: \\\\NAS\\영상).'));
} }
@ -3226,6 +3233,9 @@ class _ExplorerPageState extends State<ExplorerPage> {
} catch (_) { } catch (_) {
ok = false; ok = false;
} }
if (!ok && unc != null) {
return (null, trf('폴더가 없거나 열 수 없습니다. Windows 의 네트워크 공유는 \\\\서버\\공유 형식입니다 (바꾼 경로: {0})', [unc]));
}
return ok ? (path, null) : (null, tr('폴더가 없거나 열 수 없습니다')); return ok ? (path, null) : (null, tr('폴더가 없거나 열 수 없습니다'));
} }
@ -3870,6 +3880,24 @@ class _PatternDialogState extends State<_PatternDialog> {
/// 179: SMB 경로 (smb:// · \\서버\공유) /// 179: SMB 경로 (smb:// · \\서버\공유)
bool looksLikeSmb(String path) => RegExp(r'^(smb|cifs)://', caseSensitive: false).hasMatch(path) || path.startsWith(r'\\'); 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 탭을 쓰세요'); String get smbNotSupported => tr('네트워크 공유 (SMB) 는 아직 지원하지 않습니다. WebDAV 탭을 쓰세요');
class _PathDialog extends StatefulWidget { class _PathDialog extends StatefulWidget {

View File

@ -408,6 +408,19 @@ void main() {
expect(find.textContaining(right), findsWidgets); 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 { testWidgets('179: Android 에서 SMB 경로를 넣으면 "아직 지원하지 않음 · WebDAV" 안내와 [WebDAV 서버 추가]', (tester) async {
ExplorerPage.debugAndroidPaths = true; ExplorerPage.debugAndroidPaths = true;
addTearDown(() => ExplorerPage.debugAndroidPaths = null); addTearDown(() => ExplorerPage.debugAndroidPaths = null);