From 39b76e2423745983d0658d29b624d3dd99f7915a Mon Sep 17 00:00:00 2001 From: RyuJongJin <266630912+RyuJongJin@users.noreply.github.com> Date: Sun, 25 Oct 2026 17:21:29 +0900 Subject: [PATCH] =?UTF-8?q?M3=20ThumbPolicy:=20=EC=9B=90=EA=B2=A9=20?= =?UTF-8?q?=EC=8D=B8=EB=84=A4=EC=9D=BC=20"Wi-Fi=20=EC=97=90=EC=84=9C?= =?UTF-8?q?=EB=A7=8C"=20=EC=9D=98=20=EB=A7=9D=20=ED=8C=90=EB=8B=A8=20(?= =?UTF-8?q?=EC=A0=84=EC=97=AD=20=ED=95=A8=EC=88=98=20activeMeteredOf)=20?= =?UTF-8?q?=EC=9D=84=20PlatformServices.isMetered=20=EC=A3=BC=EC=9E=85?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - lan_scan.dart: deviceMetered (실제 - Android 만 묻고 PC 는 false, 물을 수 없으면 true) 는 그대로 함수로 두고, 바꿀 수 있는 전역 변수는 뺌 - remoteThumbsAllowedNow 는 c.services.isMetered 를 부름 (처음 값 = deviceMetered) - 시험: remote_thumbs_test 는 isMetered 를 넘김 (2회 통과) - 썸네일 캐시 상한 · 받기 허락은 이미 RemoteThumbs 생성자로 넘기는 값이라 그대로. netKindsOf (망 종류) 는 M4 LanFinder 몫 - 사용자가 보게 될 동작: 없음 Co-Authored-By: Claude Opus 5.5 --- integration_test/support/shot_services.dart | 1 + lib/core/lan_scan.dart | 6 ++---- lib/services/platform_services.dart | 7 ++++++- lib/ui/remote_thumb_view.dart | 3 +-- test/remote_thumbs_test.dart | 18 ++++++++---------- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/integration_test/support/shot_services.dart b/integration_test/support/shot_services.dart index 1a53bf2..dddd5ec 100644 --- a/integration_test/support/shot_services.dart +++ b/integration_test/support/shot_services.dart @@ -31,6 +31,7 @@ PlatformServices shotServices() { clock: r.clock, vault: r.vault, privileged: r.privileged, + isMetered: r.isMetered, updater: r.updater, usage: r.usage == null ? null : const FixedUsage(), models: r.models, diff --git a/lib/core/lan_scan.dart b/lib/core/lan_scan.dart index d4c4604..a955bca 100644 --- a/lib/core/lan_scan.dart +++ b/lib/core/lan_scan.dart @@ -41,10 +41,8 @@ Future?> _androidNetKinds() async { } /// X-plore 비교 5-1 ② (관리자 지시 10/21): 지금 쓰는 망이 데이터 요금제인지 (모바일 데이터 · 요금제 Wi-Fi). Android 만 묻고, -/// 그 밖 (PC) 은 false. 물을 수 없으면 true (받지 않는 쪽으로). 시험에서 바꾼다 -Future Function() activeMeteredOf = _androidMetered; - -Future _androidMetered() async { +/// 그 밖 (PC) 은 false. 물을 수 없으면 true (받지 않는 쪽으로). 앱은 PlatformServices.isMetered 로 부른다 (설계 M3 ThumbPolicy - 시험은 그것을 바꿈) +Future deviceMetered() async { if (!Platform.isAndroid) return false; try { return await const MethodChannel('jj_mkvmaker/android').invokeMethod('activeMetered') ?? true; diff --git a/lib/services/platform_services.dart b/lib/services/platform_services.dart index 3fde222..3230dbb 100644 --- a/lib/services/platform_services.dart +++ b/lib/services/platform_services.dart @@ -9,7 +9,7 @@ import '../app/settings.dart'; import '../app/vault_location.dart'; import '../core/shizuku.dart' show PrivilegedFs; import '../core/download_detect.dart'; -import '../core/lan_scan.dart' show LanHost; +import '../core/lan_scan.dart' show LanHost, deviceMetered; import '../platform/common/nllb_translator.dart'; import '../platform/common/opensubtitles_provider.dart'; import '../platform/common/whisper_recognizer.dart'; @@ -88,6 +88,9 @@ class PlatformServices { /// 없으면 PrivilegedFs.current 를 그대로 둔다 (시험이 건 가짜를 덮지 않게) final PrivilegedFs? privileged; + /// 지금 쓰는 망이 데이터 요금제인지 (원격 썸네일 "Wi-Fi 에서만" - 설계 M3 ThumbPolicy). 처음 값 = 실제 (Android 만 묻고 PC 는 false) + final Future Function() isMetered; + /// 이 기기 (Android · Windows · 메모리) - 기기마다 다르게 도는 곳은 이것을 본다 (M1, 처음 값 = 이 기기) final PlatformInfo platform; @@ -114,8 +117,10 @@ class PlatformServices { PlatformInfo? platform, VaultLocation? vault, this.privileged, + Future Function()? isMetered, }) : models = models ?? ModelStore(), vault = vault ?? VaultLocation.under(storage.tempDirectory), + isMetered = isMetered ?? deviceMetered, platform = platform ?? PlatformInfo.host, shell = shell ?? NoopShell(); diff --git a/lib/ui/remote_thumb_view.dart b/lib/ui/remote_thumb_view.dart index 5f35843..dcae562 100644 --- a/lib/ui/remote_thumb_view.dart +++ b/lib/ui/remote_thumb_view.dart @@ -6,7 +6,6 @@ import 'package:path/path.dart' as p; import '../app/app_controller.dart'; import '../core/file_ops.dart' show FileEntry; -import '../core/lan_scan.dart' show activeMeteredOf; import '../core/playlist.dart' show isVideoFile; import '../core/remote_stream.dart'; import '../core/remote_thumbs.dart'; @@ -83,7 +82,7 @@ Future remoteThumbsAllowedNow(AppController c) async { } final now = DateTime.now(); if (_meteredAt == null || now.difference(_meteredAt!) > const Duration(seconds: 10)) { - _metered = await activeMeteredOf(); + _metered = await c.services.isMetered(); _meteredAt = now; } return !_metered; diff --git a/test/remote_thumbs_test.dart b/test/remote_thumbs_test.dart index 6366e18..639d819 100644 --- a/test/remote_thumbs_test.dart +++ b/test/remote_thumbs_test.dart @@ -5,7 +5,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:jj_mkvmaker/app/app_controller.dart'; import 'package:jj_mkvmaker/app/settings.dart'; import 'package:jj_mkvmaker/core/file_ops.dart' show FileEntry; -import 'package:jj_mkvmaker/core/lan_scan.dart' show activeMeteredOf; import 'package:jj_mkvmaker/core/models.dart'; import 'package:jj_mkvmaker/core/remote_stream.dart'; import 'package:jj_mkvmaker/core/remote_thumbs.dart'; @@ -126,24 +125,23 @@ void main() { }); group('받아도 되는 망 (remoteThumbsAllowedNow)', () { - final was = activeMeteredOf; - tearDown(() { - activeMeteredOf = was; - forgetRemoteThumbNet(); - }); - AppController ctl() => AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); + // 설계 M3 ThumbPolicy: 망 판단은 PlatformServices.isMetered 로 넘긴다 (예전 전역 함수 activeMeteredOf) + var metered = true; + tearDown(forgetRemoteThumbNet); + AppController ctl() => AppController( + PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService(), isMetered: () async => metered)); test('Wi-Fi 에서만 (처음 값): 데이터 요금제 망이면 아니오 · 아니면 예 · 끔 · 늘', () async { final c = ctl(); - activeMeteredOf = () async => true; + metered = true; forgetRemoteThumbNet(); expect(await remoteThumbsAllowedNow(c), isFalse, reason: '모바일 데이터 (VPN 이어도 그 아래가 모바일 데이터)'); - activeMeteredOf = () async => false; + metered = false; forgetRemoteThumbNet(); expect(await remoteThumbsAllowedNow(c), isTrue, reason: 'Wi-Fi (집 Wi-Fi 위의 Tailscale 포함)'); c.settings.explorer.remoteThumbs = 'off'; expect(await remoteThumbsAllowedNow(c), isFalse); c.settings.explorer.remoteThumbs = 'always'; - activeMeteredOf = () async => true; + metered = true; forgetRemoteThumbNet(); expect(await remoteThumbsAllowedNow(c), isTrue); });