fix(android): surface mpv command failures

This commit is contained in:
edde746
2026-07-12 08:42:22 +02:00
parent 79dabaf125
commit 5876a602c3
4 changed files with 142 additions and 5 deletions
@@ -159,7 +159,7 @@ open class MpvPlayerPlugin(
"openContentFd" -> handleOpenContentFd(call, result)
"closeContentFd" -> handleCloseContentFd(call, result)
"isInitialized" -> result.success(playerCore?.isInitialized ?: false)
"setLogLevel" -> result.success(null)
"setLogLevel" -> handleSetLogLevel(call, result)
else -> result.notImplemented()
}
}
@@ -358,14 +358,30 @@ open class MpvPlayerPlugin(
val core = playerCore
if (core == null) {
result.success(null)
result.error("NOT_INITIALIZED", "Player not initialized", null)
return
}
core.command(args.toTypedArray()) {
result.success(null)
core.command(args.toTypedArray()) { success ->
if (success) {
result.success(null)
} else {
result.error("COMMAND_FAILED", "mpv command failed", args)
}
}
}
private fun handleSetLogLevel(call: MethodCall, result: MethodChannel.Result) {
if (call.argument<String>("level") == null) {
result.error("INVALID_ARGS", "Missing 'level'", null)
return
}
result.error(
"UNSUPPORTED",
"Runtime mpv log level changes are not supported on Android",
null
)
}
private fun handleSetVisible(call: MethodCall, result: MethodChannel.Result) {
val visible = call.argument<Boolean>("visible")
@@ -0,0 +1,54 @@
package com.edde746.plezy.mpv
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class MpvPlayerPluginTest {
@Test
fun commandWithoutCoreReportsNotInitialized() {
val result = RecordingResult()
MpvPlayerPlugin().onMethodCall(
MethodCall("command", mapOf("args" to listOf("seek", "1", "absolute"))),
result
)
assertEquals("NOT_INITIALIZED", result.errorCode)
assertNull(result.successValue)
}
@Test
fun setLogLevelReportsUnsupported() {
val result = RecordingResult()
MpvPlayerPlugin().onMethodCall(
MethodCall("setLogLevel", mapOf("level" to "warn")),
result
)
assertEquals("UNSUPPORTED", result.errorCode)
assertNull(result.successValue)
}
private class RecordingResult : MethodChannel.Result {
var successValue: Any? = null
var errorCode: String? = null
override fun success(result: Any?) {
successValue = result
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
this.errorCode = errorCode
}
override fun notImplemented() = Unit
}
}
+3 -1
View File
@@ -879,7 +879,9 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
_audioFocusFuture!.ignore();
}
await currentPlayer.setProperty('msg-level', debugLoggingEnabled ? 'all=debug,ffmpeg/video=warn' : 'all=error');
await currentPlayer.setLogLevel(debugLoggingEnabled ? 'v' : 'warn');
if (!Platform.isAndroid || useExoPlayer) {
await currentPlayer.setLogLevel(debugLoggingEnabled ? 'v' : 'warn');
}
await currentPlayer.setProperty('hwdec', _getHwdecValue(enableHardwareDecoding));
await currentPlayer.setProperty(
+65
View File
@@ -0,0 +1,65 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:plezy/mpv/player/player_native.dart';
import 'package:plezy/services/settings_service.dart';
import '../test_helpers/mock_player_channels.dart';
import '../test_helpers/prefs.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() async {
resetSharedPreferencesForTest();
SettingsService.resetForTesting();
await SettingsService.getInstance();
});
test('Android command failure reaches seek recovery', () async {
await withMockPlayerChannels(
methodChannelName: 'com.plezy/mpv_player',
eventChannelName: 'com.plezy/mpv_player/events',
methodHandler: (call) async {
if (call.method == 'initialize') return true;
if (call.method == 'command') {
throw PlatformException(code: 'COMMAND_FAILED', message: 'mpv command failed');
}
return null;
},
testBody: () async {
final player = PlayerNative();
try {
await player.seek(const Duration(seconds: 12));
expect(player.state.position, Duration.zero);
} finally {
await player.dispose();
}
},
);
});
test('Android setLogLevel failure is exposed to Dart', () async {
await withMockPlayerChannels(
methodChannelName: 'com.plezy/mpv_player',
eventChannelName: 'com.plezy/mpv_player/events',
methodHandler: (call) async {
if (call.method == 'initialize') return true;
if (call.method == 'setLogLevel') {
throw PlatformException(code: 'UNSUPPORTED');
}
return null;
},
testBody: () async {
final player = PlayerNative();
try {
await expectLater(
player.setLogLevel('warn'),
throwsA(isA<PlatformException>().having((error) => error.code, 'code', 'UNSUPPORTED')),
);
} finally {
await player.dispose();
}
},
);
});
}