fix(player): clear stale tracks before opening media

close #1028
This commit is contained in:
edde746
2026-05-13 09:22:17 +02:00
parent 8aea796dbd
commit 12073ea134
3 changed files with 102 additions and 0 deletions
@@ -99,6 +99,7 @@ class PlayerAndroid extends PlayerBase {
if (disposed) return;
await _ensureInitialized();
final startPosition = media.start ?? Duration.zero;
clearTracks();
resetPlaybackProgress(startPosition);
setSeekable(false);
+1
View File
@@ -105,6 +105,7 @@ class PlayerNative extends PlayerBase {
if (disposed) return;
await _ensureInitialized();
final startPosition = media.start ?? Duration.zero;
clearTracks();
resetPlaybackProgress(startPosition);
setSeekable(false);
+100
View File
@@ -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<void> _withMockChannels({
required String methodChannelName,
required String eventChannelName,
required Future<void> 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},
]);
}