From 07ec8cd4a5fefa402853b2ebea12d0b401bf1fa5 Mon Sep 17 00:00:00 2001 From: RyuJongJin <266630912+RyuJongJin@users.noreply.github.com> Date: Sat, 3 Oct 2026 04:49:33 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9B=B9=20=EB=B8=8C=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=EC=A0=80:=20=EB=8B=A4=EB=A5=B8=20=ED=99=94=EB=A9=B4=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=80=EB=A9=B4=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EC=9D=98=20=EB=8F=99=EC=98=81=EC=83=81=20=C2=B7=20=EC=86=8C?= =?UTF-8?q?=EB=A6=AC=20=EB=A9=88=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 브라우저에서 [다운로드] → 목록 보기 (또는 플레이어 · 설정 등) 로 가면 가려진 브라우저에서 YouTube 가 계속 재생되어 소리만 났다. 브라우저 위에 다른 화면이 올라오면 (RouteAware.didPushNext) 페이지의 video · audio 를 멈춘다 (WebNav.pauseMedia, Edge · Chrome · Android 웹뷰 공통). Co-Authored-By: Claude Opus 5.5 --- lib/main.dart | 1 + lib/ui/browser_page.dart | 39 ++++++++++++++++++++++++++++++++++++++- test/browser_test.dart | 3 +++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/main.dart b/lib/main.dart index d9d1907..395a232 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -507,6 +507,7 @@ class JjCapCutApp extends StatelessWidget { theme: buildTheme(), navigatorKey: navigatorKey, scaffoldMessengerKey: messengerKey, + navigatorObservers: [browserRouteObserver], // 모든 화면에: 환경 설정 · 종료 버튼이 쓸 것 + 끌어다 놓은 동영상을 목록에 추가 builder: (context, child) => AppScope( controller: controller, diff --git a/lib/ui/browser_page.dart b/lib/ui/browser_page.dart index 222dfe6..69e6141 100644 --- a/lib/ui/browser_page.dart +++ b/lib/ui/browser_page.dart @@ -30,8 +30,17 @@ abstract class WebNav { /// YouTube · Google 로그인 쿠키를 yt-dlp 용 cookies.txt 로 내보내기 Future exportCookies(); + + /// 페이지의 동영상 · 소리 멈추기 (다른 화면으로 갈 때) + Future pauseMedia(); } +/// 페이지의 모든 동영상 · 소리를 멈추는 스크립트 +const pauseMediaScript = "document.querySelectorAll('video,audio').forEach(function(m){try{m.pause()}catch(e){}})"; + +/// 화면 이동 알림 (브라우저 위에 다른 화면이 올라오면 동영상을 멈춘다). MaterialApp.navigatorObservers 에 넣는다. +final browserRouteObserver = RouteObserver>(); + /// 웹뷰 → 화면으로 알리는 통로 class BrowserHost { final void Function(String url) onUrl; @@ -97,7 +106,7 @@ class BrowserPage extends StatefulWidget { } } -class _BrowserPageState extends State { +class _BrowserPageState extends State with RouteAware { late String _url = widget.initialUrl ?? widget.c.settings.homeUrl; String _title = ''; double _progress = 1; @@ -138,8 +147,22 @@ class _BrowserPageState extends State { BrowserPage._open++; } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + final route = ModalRoute.of(context); + if (route != null) browserRouteObserver.subscribe(this, route); + } + + /// 다운로드 목록 · 플레이어 · 설정 등이 위에 올라왔다: 보던 동영상을 멈춘다 (가려진 채 소리만 나지 않게) + @override + void didPushNext() { + _nav?.pauseMedia(); + } + @override void dispose() { + browserRouteObserver.unsubscribe(this); BrowserPage._open--; _address.dispose(); _addressFocus.dispose(); @@ -492,6 +515,13 @@ class _InAppNav implements WebNav { final String dataDir; _InAppNav(this.c, this.env, this.dataDir); + @override + Future pauseMedia() async { + try { + await c.evaluateJavascript(source: pauseMediaScript); + } catch (_) {} + } + static const _cookieSites = [ 'https://www.youtube.com/', 'https://m.youtube.com/', @@ -642,6 +672,13 @@ class _CefNav implements WebNav { final String dataDir; _CefNav(this.c, this.dataDir); + @override + Future pauseMedia() async { + try { + await c.evaluateJavascript(pauseMediaScript); + } catch (_) {} + } + /// YouTube · Google 쿠키를 yt-dlp 용 cookies.txt 로 (Edge 와 같은 파일 - 지금 쓰는 엔진의 로그인이 쓰인다). /// Chrome 엔진은 쿠키의 이름 · 값만 알려 주므로 만료 · 보안 표시는 기본값으로 적는다. @override diff --git a/test/browser_test.dart b/test/browser_test.dart index c21d00f..683c2d9 100644 --- a/test/browser_test.dart +++ b/test/browser_test.dart @@ -40,6 +40,9 @@ class _Backend implements DownloadBackend { } class _Nav implements WebNav { + int paused = 0; + @override + Future pauseMedia() async => paused++; final loads = []; @override Future load(String url) async => loads.add(url);