124 lines
5.2 KiB
Dart
124 lines
5.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../app/app_controller.dart';
|
|
import '../core/file_ops.dart' show formatSize;
|
|
import '../core/vfs.dart';
|
|
import '../core/webdav.dart' show DavRegistry;
|
|
import '../l10n/tr.dart';
|
|
import 'theme.dart';
|
|
|
|
/// WebDAV 파일은 열거나 재생하려면 임시 폴더로 받는다 (받는 동안 진행 창 · 취소). 로컬은 그대로. 받지 못하면 null.
|
|
Future<String?> fetchDav(BuildContext context, AppController c, String path) async {
|
|
if (!isDav(path)) return path;
|
|
final temp = await c.services.storage.tempDirectory();
|
|
if (!context.mounted) return null;
|
|
final messenger = ScaffoldMessenger.maybeOf(context);
|
|
final nav = Navigator.of(context);
|
|
final progress = ValueNotifier<(int, int)>((0, 0));
|
|
var cancelled = false;
|
|
final dialog = showDialog<void>(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (ctx) => AlertDialog(
|
|
scrollable: true,
|
|
title: Text(isSftp(path) ? tr('SFTP 에서 받는 중') : isSmb(path) ? tr('SMB 에서 받는 중') : tr('WebDAV 에서 받는 중')),
|
|
content: ValueListenableBuilder<(int, int)>(
|
|
valueListenable: progress,
|
|
builder: (_, v, _) => Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
|
Text(vBasename(path)),
|
|
const SizedBox(height: 10),
|
|
LinearProgressIndicator(value: v.$2 > 0 ? v.$1 / v.$2 : null),
|
|
const SizedBox(height: 6),
|
|
Text('${formatSize(v.$1)}${v.$2 > 0 ? ' / ${formatSize(v.$2)}' : ''}',
|
|
style: TextStyle(fontSize: 12, color: JjColors.textDim)),
|
|
]),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
cancelled = true;
|
|
Navigator.pop(ctx);
|
|
},
|
|
child: Text(tr('취소')),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
try {
|
|
final local = await vLocalCopy(path, temp, onProgress: (d, t) => progress.value = (d, t));
|
|
return cancelled ? null : local;
|
|
} catch (e) {
|
|
if (!cancelled) messenger?.showSnackBar(SnackBar(content: Text(trf('받지 못했습니다: {0}', [e]))));
|
|
return null;
|
|
} finally {
|
|
if (!cancelled && nav.mounted) nav.pop();
|
|
await dialog;
|
|
progress.dispose();
|
|
}
|
|
}
|
|
|
|
/// 로그인이 필요한 WebDAV 서버의 파일인지 (아이디나 비밀번호가 있음)
|
|
bool davNeedsLogin(String path) {
|
|
if (!isWebDav(path)) return false;
|
|
final s = DavRegistry.server(DavPath.parse(path).server);
|
|
return s != null && (s.user.isNotEmpty || s.password.isNotEmpty);
|
|
}
|
|
|
|
/// 외부 프로그램 · 다른 앱으로 연다 ([program]: 'system' 이나 실행 파일).
|
|
/// 55: 다른 앱으로 넘기는 주소에는 아이디 · 비밀번호를 넣지 않는다. 그래서 로그인이 필요한 WebDAV 파일이면
|
|
/// 조용히 실패하지 않게 먼저 묻는다: [받아서 열기] (임시로 받은 뒤 열기 - 어느 앱이든 열림) · [주소로 열기] (받는 앱이 아이디를 물어야 열림).
|
|
Future<void> openExternalPlayable(BuildContext context, AppController c, String program, List<String> files) async {
|
|
// 88: SFTP 는 다른 앱에 넘길 주소가 없다 - 늘 받아서 연다 (묻지 않음)
|
|
if (files.any(noStreamUrl)) {
|
|
final local = <String>[];
|
|
for (final f in files) {
|
|
if (!context.mounted) return;
|
|
final l = noStreamUrl(f) ? await fetchDav(context, c, f) : vPlayable(f);
|
|
if (l == null) return; // 취소 · 실패
|
|
local.add(l);
|
|
}
|
|
await c.services.shell.openExternal(program, local);
|
|
return;
|
|
}
|
|
if (!files.any(davNeedsLogin)) {
|
|
await c.services.shell.openExternal(program, files.map(vPlayable).toList());
|
|
return;
|
|
}
|
|
// 102: 환경 설정 (매번 묻기 · 늘 받아서 · 늘 주소로)
|
|
final pick = switch (c.settings.remote.davExternalOpen) {
|
|
'fetch' => 'fetch',
|
|
'url' => 'url',
|
|
_ => await showDialog<String>(
|
|
context: context,
|
|
builder: (ctx) => AlertDialog(
|
|
scrollable: true,
|
|
title: Text(tr('이 WebDAV 서버는 로그인이 필요합니다')),
|
|
content: SizedBox(
|
|
width: 520,
|
|
child: Text(tr('다른 앱에는 아이디 · 비밀번호를 넘기지 않습니다.\n\n'
|
|
'• 받아서 열기: 임시 폴더로 받은 뒤 엽니다. 어느 앱에서나 열리지만 다 받을 때까지 기다려야 합니다.\n'
|
|
'• 주소로 열기: 받지 않고 바로 재생합니다. 그 앱이 아이디 · 비밀번호를 물으면 넣어 주세요 (VLC 등). '
|
|
'묻지 않는 앱에서는 열리지 않습니다.')),
|
|
),
|
|
actions: [
|
|
TextButton(onPressed: () => Navigator.pop(ctx), child: Text(tr('취소'))),
|
|
TextButton(onPressed: () => Navigator.pop(ctx, 'url'), child: Text(tr('주소로 열기'))),
|
|
FilledButton(onPressed: () => Navigator.pop(ctx, 'fetch'), child: Text(tr('받아서 열기'))),
|
|
],
|
|
),
|
|
),
|
|
};
|
|
if (pick == 'url') {
|
|
await c.services.shell.openExternal(program, files.map(vPlayable).toList());
|
|
} else if (pick == 'fetch') {
|
|
final local = <String>[];
|
|
for (final f in files) {
|
|
if (!context.mounted) return;
|
|
final l = await fetchDav(context, c, f);
|
|
if (l == null) return; // 취소 · 실패
|
|
local.add(l);
|
|
}
|
|
await c.services.shell.openExternal(program, local);
|
|
}
|
|
}
|