175 lines
5.3 KiB
Swift
175 lines
5.3 KiB
Swift
#if os(iOS) || os(tvOS)
|
|
import Flutter
|
|
#elseif os(macOS)
|
|
import FlutterMacOS
|
|
#endif
|
|
|
|
/// Protocol for shared MpvPlayerPlugin method handlers across iOS, tvOS, and macOS.
|
|
/// Platform-specific methods (PiP, initialization, window finding) remain
|
|
/// in the per-platform MpvPlayerPlugin files.
|
|
protocol MpvPluginShared: AnyObject, MpvPlayerDelegate {
|
|
var coreBase: MpvPlayerCoreBase? { get }
|
|
var eventSink: FlutterEventSink? { get }
|
|
var nameToId: [String: Int] { get set }
|
|
|
|
func setPlayerVisible(_ visible: Bool, restoreOnWindowVisible: Bool)
|
|
func updatePlayerFrame()
|
|
|
|
/// Invoked after the `pause` property is applied via setProperty so each
|
|
/// platform can sync its PiP/idle bookkeeping (iOS: invalidate the PiP
|
|
/// playback state + timebase; macOS: setPlaying/setPaused).
|
|
func didSetPauseProperty(value: String)
|
|
}
|
|
|
|
extension MpvPluginShared {
|
|
|
|
func handleSetProperty(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let name = args["name"] as? String,
|
|
let value = args["value"] as? String
|
|
else {
|
|
result(
|
|
FlutterError(
|
|
code: "INVALID_ARGS", message: "Missing 'name' or 'value' argument",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
guard let core = coreBase else {
|
|
result(
|
|
FlutterError(
|
|
code: "NOT_INITIALIZED", message: "MPV player is not initialized", details: nil))
|
|
return
|
|
}
|
|
|
|
core.setPropertyAsync(name, value: value) { [weak self] propertyResult in
|
|
switch propertyResult {
|
|
case .success:
|
|
if name == "pause" {
|
|
self?.didSetPauseProperty(value: value)
|
|
}
|
|
result(nil)
|
|
case .failure(let error):
|
|
let lifecycleUnavailable = error is MpvLifecycleUnavailableError
|
|
result(
|
|
FlutterError(
|
|
code: lifecycleUnavailable ? "NOT_INITIALIZED" : "SET_PROPERTY_FAILED",
|
|
message:
|
|
lifecycleUnavailable
|
|
? "MPV player is not initialized"
|
|
: "MPV rejected or cancelled the property write",
|
|
details: nil))
|
|
}
|
|
}
|
|
}
|
|
|
|
func handleGetProperty(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let name = args["name"] as? String
|
|
else {
|
|
result(
|
|
FlutterError(code: "INVALID_ARGS", message: "Missing 'name' argument", details: nil)
|
|
)
|
|
return
|
|
}
|
|
coreBase?.getPropertyAsync(name) { propertyResult in
|
|
switch propertyResult {
|
|
case .success(let value):
|
|
result(value)
|
|
case .failure:
|
|
result(nil)
|
|
}
|
|
} ?? result(nil)
|
|
}
|
|
|
|
func handleObserveProperty(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let name = args["name"] as? String,
|
|
let format = args["format"] as? String,
|
|
let id = args["id"] as? Int
|
|
else {
|
|
result(
|
|
FlutterError(
|
|
code: "INVALID_ARGS", message: "Missing 'name', 'format', or 'id' argument",
|
|
details: nil))
|
|
return
|
|
}
|
|
|
|
nameToId[name] = id
|
|
coreBase?.observeProperty(name, format: format)
|
|
result(nil)
|
|
}
|
|
|
|
func handleCommand(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let commandArgs = args["args"] as? [String]
|
|
else {
|
|
result(
|
|
FlutterError(code: "INVALID_ARGS", message: "Missing 'args' argument", details: nil)
|
|
)
|
|
return
|
|
}
|
|
|
|
coreBase?.commandAsync(commandArgs) { commandResult in
|
|
switch commandResult {
|
|
case .success:
|
|
result(nil)
|
|
case .failure(let error):
|
|
result(
|
|
FlutterError(
|
|
code: "COMMAND_FAILED", message: error.localizedDescription, details: nil))
|
|
}
|
|
} ?? result(nil)
|
|
}
|
|
|
|
func handleSetVisible(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let visible = args["visible"] as? Bool
|
|
else {
|
|
result(
|
|
FlutterError(
|
|
code: "INVALID_ARGS", message: "Missing 'visible' argument", details: nil))
|
|
return
|
|
}
|
|
let restoreOnWindowVisible = args["restoreOnWindowVisible"] as? Bool ?? false
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
self?.setPlayerVisible(visible, restoreOnWindowVisible: restoreOnWindowVisible)
|
|
if visible { self?.updatePlayerFrame() }
|
|
result(nil)
|
|
}
|
|
}
|
|
|
|
func handleUpdateFrame(result: @escaping FlutterResult) {
|
|
DispatchQueue.main.async { [weak self] in
|
|
self?.updatePlayerFrame()
|
|
result(nil)
|
|
}
|
|
}
|
|
|
|
func handleSetLogLevel(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
guard let args = call.arguments as? [String: Any],
|
|
let level = args["level"] as? String
|
|
else {
|
|
result(FlutterError(code: "INVALID_ARGS", message: "Missing 'level'", details: nil))
|
|
return
|
|
}
|
|
coreBase?.setLogLevel(level)
|
|
result(nil)
|
|
}
|
|
|
|
// MARK: - MpvPlayerDelegate
|
|
|
|
func onPropertyChange(name: String, value: Any?) {
|
|
guard let eventSink = eventSink, let propId = nameToId[name] else { return }
|
|
eventSink([propId, value as Any])
|
|
}
|
|
|
|
func onEvent(name: String, data: [String: Any]?) {
|
|
guard let eventSink = eventSink else { return }
|
|
var event: [String: Any] = ["type": "event", "name": name]
|
|
if let data = data { event["data"] = data }
|
|
eventSink(event)
|
|
}
|
|
}
|