151 lines
7.0 KiB
Dart
151 lines
7.0 KiB
Dart
/// 약속: 시작에 쓰는 도우미 - 다른 앱에서 연 파일 · DNS 강제 · Tor 준비 · DNS 실패 알림 · 새 재생 창 판단 · 지금 버전 · 마지막 작업 글.
|
|
library;
|
|
|
|
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
import '../app/app_controller.dart';
|
|
import '../app/net_guard.dart';
|
|
import '../app/tor_service.dart';
|
|
import '../app/web_proxy.dart';
|
|
import '../app/settings.dart';
|
|
import '../core/playlist.dart';
|
|
import '../core/sftp.dart' show SftpRegistry;
|
|
import '../core/ftp.dart' show FtpRegistry;
|
|
import '../core/smb.dart' show SmbRemote;
|
|
import '../services/platform_services.dart';
|
|
import '../ui/network_settings.dart' show DnsFailureNotice;
|
|
import '../ui/player_page.dart';
|
|
import '../l10n/tr.dart';
|
|
import '../ui/notice.dart';
|
|
|
|
/// Android: 다른 앱 (파일 앱 "다음으로 열기" · 공유) 에서 받은 동영상.
|
|
/// 설정 "다른 앱에서 동영상을 열 때" 에 따라 바로 재생 (기본) 또는 MKV 목록에 추가. 실제 경로를 모르는 것은 재생만.
|
|
Future<void> openFromOtherApp(
|
|
GlobalKey<NavigatorState> navigatorKey, AppController c, List<Map<Object?, Object?>> files) async {
|
|
final paths = [for (final f in files) f['path'] as String?].whereType<String>().toList();
|
|
final uris = [
|
|
for (final f in files)
|
|
if (f['path'] == null) f['uri'] as String,
|
|
];
|
|
c.note(trf('다른 앱에서 열기: {0}', [files.map((f) => f['name'] ?? f['uri']).join(', ')]));
|
|
// MKV 화면으로 돌아온 뒤 (재생 중이었다면 그 화면은 닫는다)
|
|
navigatorKey.currentState?.popUntil((r) => r.isFirst);
|
|
final ctx = navigatorKey.currentContext;
|
|
if (ctx == null || !ctx.mounted) return;
|
|
if (paths.isNotEmpty) {
|
|
final added = c.addVideos(paths, allowOutputFolder: true);
|
|
if (c.settings.playback.openFileAction == 'add') {
|
|
await added;
|
|
final first = c.videos.where((v) => paths.any((f) => p.equals(f, v.path))).firstOrNull;
|
|
if (first != null) c.select(first);
|
|
} else {
|
|
unawaited(added);
|
|
await playFiles(ctx, c, paths);
|
|
}
|
|
} else if (uris.isNotEmpty) {
|
|
// 파일 위치를 알려 주지 않는 앱: 재생만 (MKV 만들기 · 자막은 파일 고르기로 추가해야 함)
|
|
ScaffoldMessenger.maybeOf(ctx)?.showSnackBar(SnackBar(
|
|
content: Text(tr('파일 위치를 알 수 없어 재생만 합니다. MKV 를 만들려면 "동영상 추가" 로 고르세요.'))));
|
|
final create = c.services.createMediaPlayer;
|
|
if (create != null) {
|
|
await Navigator.of(ctx).push(MaterialPageRoute<void>(
|
|
builder: (_) => PlayerPage(c: c, player: create(), files: uris, start: 0)));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// DNS 강제 (사용자 요청 10/10) · Tor 준비: 앱의 HTTP 가 정한 DNS 를 쓰게 하고, 설정이 바뀌면 다시 건다.
|
|
/// [second] (재생만 하는 새 창) 은 설정을 읽기만 한다
|
|
Future<void> setupNetwork(AppController c, String dataDir, {bool second = false}) async {
|
|
final g = NetGuard.instance
|
|
..config = (() => c.settings.network.config)
|
|
..log = c.note;
|
|
try {
|
|
await g.ensure();
|
|
} catch (e) {
|
|
c.note(trf('DNS 강제 프록시를 시작하지 못했습니다: {0}', [e]));
|
|
}
|
|
HttpOverrides.global = JjHttpOverrides();
|
|
// 88: SFTP 는 HTTP 가 아니라 직접 잇는다 - 같은 DNS 설정을 따르게 (집 안 이름 · 주소는 기기 DNS)
|
|
SftpRegistry.connect = SftpRegistry.connectWithDns(g.resolver);
|
|
FtpRegistry.resolve = FtpRegistry.resolveWithDns(g.resolver);
|
|
SmbRemote.resolve = FtpRegistry.resolveWithDns(g.resolver);
|
|
if (second) return;
|
|
TorService.dataDir = dataDir;
|
|
TorService.instance.log = c.note;
|
|
if (Platform.isAndroid) {
|
|
WebProxy.log = c.note;
|
|
// 환경 설정 "다른 화면으로 가면 소리 멈춤" 이 꺼져 있으면 일반 브라우저가 뒤에 있어도 DNS 문을 열어 둔다
|
|
WebProxy.keepPlayingWhenHidden = () => !c.settings.browser.pauseOnLeave;
|
|
try {
|
|
await WebProxy.apply();
|
|
} catch (e) {
|
|
c.note(trf('웹 뷰 프록시를 걸지 못했습니다: {0}', [e]));
|
|
}
|
|
// 앱이 뒤로 가면 웹 뷰 프록시 문을 닫고, 돌아오면 다시 (관리자 결정 10/11 - 열려 있는 시간을 줄인다)
|
|
AppLifecycleListener(
|
|
onResume: () => unawaited(WebProxy.setForeground(true)),
|
|
onHide: () => unawaited(WebProxy.setForeground(false)),
|
|
);
|
|
}
|
|
var last = c.settings.network.config.key;
|
|
var lastPause = c.settings.browser.pauseOnLeave;
|
|
c.addListener(() async {
|
|
if (Platform.isAndroid && c.settings.browser.pauseOnLeave != lastPause) {
|
|
lastPause = c.settings.browser.pauseOnLeave;
|
|
await WebProxy.apply();
|
|
}
|
|
final now = c.settings.network.config.key;
|
|
if (now == last) return;
|
|
last = now;
|
|
c.note(trf('DNS 설정: {0}', [c.settings.network.mode == 'system' ? tr('시스템 DNS') : c.settings.network.config.label]));
|
|
await g.ensure();
|
|
if (Platform.isAndroid) await WebProxy.apply();
|
|
});
|
|
}
|
|
|
|
/// DNS 서버에 닿지 않으면 알린다 (기기 DNS 로 몰래 바꾸지 않음). [시스템 DNS 로 바꾸기] 를 함께
|
|
void watchDnsFailure(GlobalKey<ScaffoldMessengerState> messengerKey, AppController c) {
|
|
NetGuard.instance.failure.addListener(() {
|
|
final f = NetGuard.instance.failure.value;
|
|
if (f == null) return;
|
|
// 248: 같은 안내를 보이는 화면 (브라우저 덮개 · 설정의 DNS 칸) 이 열려 있으면 띄우지 않는다 (상태는 설정 · 작업 기록 줄에)
|
|
if (NetGuard.instance.noticeBlockers.value > 0) return;
|
|
showNotice(
|
|
messengerKey.currentState,
|
|
// 248: 버튼이 있는 안내라 사용자가 닫거나 누를 때까지 둔다 (한 번만 뜨고, 상태는 설정 · 작업 현황에 남음 - 249)
|
|
life: NoticeLife.untilClosed,
|
|
// 사용자에게는 무엇이 안 되고 무엇을 할지만 (예외 원문은 작업 기록에 - NetGuard.reportError).
|
|
// 상태가 풀리거나 같은 안내를 보이는 화면이 열리면 스스로 닫힌다 (248)
|
|
content: DnsFailureNotice(failure: f),
|
|
action: (tr('시스템 DNS 로 바꾸기'), () => c.updateSettings((x) => x.network.mode = 'system')),
|
|
);
|
|
});
|
|
}
|
|
|
|
/// 탐색기에서 연 동영상을 새 재생 창으로 띄울지 (재생 요청 + 설정 "새 창에서")
|
|
bool wantsNewPlayerWindow(LaunchRequest r, AppSettings s) =>
|
|
s.playback.openFileWindow == 'new' &&
|
|
r.files.any(isVideoFile) &&
|
|
(r.action == LaunchAction.play || (r.action == LaunchAction.add && s.playback.openFileAction == 'play'));
|
|
|
|
/// 지금 실행 중인 버전 (예: 2026.10.05_003). 모르면 ''.
|
|
Future<String> currentAppVersion(PlatformServices services) async {
|
|
try {
|
|
return await services.updater?.currentVersion() ?? '';
|
|
} catch (_) {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/// 종료 창에 보일 "바로 전 작업": 하던 작업, 없으면 마지막 작업 기록 (시각 표시는 뺀다)
|
|
String lastWorkText(AppController c) {
|
|
if (c.currentJob != null) return c.currentJob!;
|
|
if (c.logs.isEmpty) return tr('없음');
|
|
return c.logs.last.replaceFirst(RegExp(r'^\[[\d:]+\]\s*'), '').split('\n').first;
|
|
}
|