From 12073ea134ab3f8b2a04a634e2137dda63be8b82 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Wed, 13 May 2026 08:42:36 +0200 Subject: [PATCH] fix(player): clear stale tracks before opening media close #1028 --- lib/mpv/player/platform/player_android.dart | 1 + lib/mpv/player/player_native.dart | 1 + test/mpv/player_open_test.dart | 100 ++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 test/mpv/player_open_test.dart diff --git a/lib/mpv/player/platform/player_android.dart b/lib/mpv/player/platform/player_android.dart index f4d844ac..c5d05047 100644 --- a/lib/mpv/player/platform/player_android.dart +++ b/lib/mpv/player/platform/player_android.dart @@ -99,6 +99,7 @@ class PlayerAndroid extends PlayerBase { if (disposed) return; await _ensureInitialized(); final startPosition = media.start ?? Duration.zero; + clearTracks(); resetPlaybackProgress(startPosition); setSeekable(false); diff --git a/lib/mpv/player/player_native.dart b/lib/mpv/player/player_native.dart index ceac4209..be780115 100644 --- a/lib/mpv/player/player_native.dart +++ b/lib/mpv/player/player_native.dart @@ -105,6 +105,7 @@ class PlayerNative extends PlayerBase { if (disposed) return; await _ensureInitialized(); final startPosition = media.start ?? Duration.zero; + clearTracks(); resetPlaybackProgress(startPosition); setSeekable(false); diff --git a/test/mpv/player_open_test.dart b/test/mpv/player_open_test.dart new file mode 100644 index 00000000..aeede48b --- /dev/null +++ b/test/mpv/player_open_test.dart @@ -0,0 +1,100 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:plezy/mpv/mpv.dart'; +import 'package:plezy/mpv/player/platform/player_android.dart'; +import 'package:plezy/mpv/player/player_native.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('player open', () { + test('ExoPlayer clears stale Dart track state before opening new media', () async { + await _withMockChannels( + methodChannelName: 'com.plezy/exo_player', + eventChannelName: 'com.plezy/exo_player/events', + testBody: () async { + final player = PlayerAndroid(); + try { + _seedTracks(player); + expect(player.state.tracks.audio, isNotEmpty); + expect(player.state.track.audio, isNotNull); + + await player.open(Media('https://example.test/next.mkv')); + + expect(player.state.tracks.audio, isEmpty); + expect(player.state.tracks.subtitle, isEmpty); + expect(player.state.track.audio, isNull); + expect(player.state.track.subtitle, isNull); + } finally { + await player.dispose(); + } + }, + ); + }); + + test('MPV clears stale Dart track state before opening new media', () async { + await _withMockChannels( + methodChannelName: 'com.plezy/mpv_player', + eventChannelName: 'com.plezy/mpv_player/events', + testBody: () async { + final player = PlayerNative(); + try { + _seedTracks(player); + expect(player.state.tracks.audio, isNotEmpty); + expect(player.state.track.audio, isNotNull); + + await player.open(Media('https://example.test/next.mkv')); + + expect(player.state.tracks.audio, isEmpty); + expect(player.state.tracks.subtitle, isEmpty); + expect(player.state.track.audio, isNull); + expect(player.state.track.subtitle, isNull); + } finally { + await player.dispose(); + } + }, + ); + }); + }); +} + +Future _withMockChannels({ + required String methodChannelName, + required String eventChannelName, + required Future Function() testBody, +}) async { + final messenger = TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; + final methodChannel = MethodChannel(methodChannelName); + final eventChannel = MethodChannel(eventChannelName); + + messenger.setMockMethodCallHandler(methodChannel, (call) async { + switch (call.method) { + case 'initialize': + return true; + case 'observeProperty': + case 'setVisible': + case 'setProperty': + case 'command': + case 'open': + case 'dispose': + return null; + default: + return null; + } + }); + messenger.setMockMethodCallHandler(eventChannel, (call) async => null); + + try { + await testBody(); + } finally { + messenger.setMockMethodCallHandler(methodChannel, null); + messenger.setMockMethodCallHandler(eventChannel, null); + } +} + +void _seedTracks(dynamic player) { + player.handlePropertyChange('track-list', const [ + {'type': 'audio', 'id': '2_0', 'title': 'English', 'lang': 'eng', 'selected': true}, + {'type': 'sub', 'id': '3_0', 'title': 'English', 'lang': 'eng', 'selected': true}, + ]); +}