2019-11-30 15:08:32 +00:00
|
|
|
/*
|
|
|
|
* This file is part of mpv.
|
|
|
|
*
|
|
|
|
* mpv is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* mpv is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
import MediaPlayer
|
|
|
|
|
|
|
|
class RemoteCommandCenter: NSObject {
|
|
|
|
enum KeyType {
|
|
|
|
case normal
|
|
|
|
case repeatable
|
|
|
|
}
|
|
|
|
|
2024-02-06 20:04:37 +00:00
|
|
|
struct Config {
|
|
|
|
let key: Int32
|
|
|
|
let type: KeyType
|
|
|
|
var state: UInt32 = 0
|
|
|
|
|
|
|
|
init(key: Int32, type: KeyType = .normal) {
|
|
|
|
self.key = key
|
|
|
|
self.type = type
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var configs: [MPRemoteCommand:Config] = [
|
|
|
|
MPRemoteCommandCenter.shared().pauseCommand: Config(key: MP_KEY_PAUSEONLY),
|
|
|
|
MPRemoteCommandCenter.shared().playCommand: Config(key: MP_KEY_PLAYONLY),
|
|
|
|
MPRemoteCommandCenter.shared().stopCommand: Config(key: MP_KEY_STOP),
|
|
|
|
MPRemoteCommandCenter.shared().nextTrackCommand: Config(key: MP_KEY_NEXT),
|
|
|
|
MPRemoteCommandCenter.shared().previousTrackCommand: Config(key: MP_KEY_PREV),
|
|
|
|
MPRemoteCommandCenter.shared().togglePlayPauseCommand: Config(key: MP_KEY_PLAY),
|
|
|
|
MPRemoteCommandCenter.shared().seekForwardCommand:
|
|
|
|
Config(key: MP_KEY_FORWARD, type: .repeatable),
|
|
|
|
MPRemoteCommandCenter.shared().seekBackwardCommand:
|
|
|
|
Config(key: MP_KEY_REWIND, type: .repeatable)
|
2019-11-30 15:08:32 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
var nowPlayingInfo: [String: Any] = [
|
|
|
|
MPNowPlayingInfoPropertyMediaType: NSNumber(value: MPNowPlayingInfoMediaType.video.rawValue),
|
|
|
|
MPNowPlayingInfoPropertyDefaultPlaybackRate: NSNumber(value: 1),
|
|
|
|
MPNowPlayingInfoPropertyPlaybackProgress: NSNumber(value: 0.0),
|
|
|
|
MPMediaItemPropertyPlaybackDuration: NSNumber(value: 0),
|
|
|
|
MPMediaItemPropertyTitle: "mpv",
|
|
|
|
MPMediaItemPropertyAlbumTitle: "mpv",
|
|
|
|
MPMediaItemPropertyArtist: "mpv",
|
|
|
|
]
|
|
|
|
|
|
|
|
let disabledCommands: [MPRemoteCommand] = [
|
|
|
|
MPRemoteCommandCenter.shared().changePlaybackRateCommand,
|
|
|
|
MPRemoteCommandCenter.shared().changeRepeatModeCommand,
|
|
|
|
MPRemoteCommandCenter.shared().changeShuffleModeCommand,
|
|
|
|
MPRemoteCommandCenter.shared().skipForwardCommand,
|
|
|
|
MPRemoteCommandCenter.shared().skipBackwardCommand,
|
|
|
|
MPRemoteCommandCenter.shared().changePlaybackPositionCommand,
|
|
|
|
MPRemoteCommandCenter.shared().enableLanguageOptionCommand,
|
|
|
|
MPRemoteCommandCenter.shared().disableLanguageOptionCommand,
|
|
|
|
MPRemoteCommandCenter.shared().ratingCommand,
|
|
|
|
MPRemoteCommandCenter.shared().likeCommand,
|
|
|
|
MPRemoteCommandCenter.shared().dislikeCommand,
|
|
|
|
MPRemoteCommandCenter.shared().bookmarkCommand,
|
|
|
|
]
|
|
|
|
|
2020-01-21 18:14:00 +00:00
|
|
|
var mpInfoCenter: MPNowPlayingInfoCenter { get { return MPNowPlayingInfoCenter.default() } }
|
|
|
|
var isPaused: Bool = false { didSet { updatePlaybackState() } }
|
2019-11-30 15:08:32 +00:00
|
|
|
|
2020-02-15 15:13:37 +00:00
|
|
|
@objc override init() {
|
2019-11-30 15:08:32 +00:00
|
|
|
super.init()
|
|
|
|
|
|
|
|
for cmd in disabledCommands {
|
|
|
|
cmd.isEnabled = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func start() {
|
2024-02-06 20:04:37 +00:00
|
|
|
for (cmd, _) in configs {
|
2019-11-30 15:08:32 +00:00
|
|
|
cmd.isEnabled = true
|
|
|
|
cmd.addTarget { [unowned self] event in
|
|
|
|
return self.cmdHandler(event)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-09 17:57:41 +00:00
|
|
|
if let app = NSApp as? Application, let icon = app.getMPVIcon() {
|
2020-02-15 15:13:37 +00:00
|
|
|
let albumArt = MPMediaItemArtwork(boundsSize: icon.size) { _ in
|
2019-11-30 15:08:32 +00:00
|
|
|
return icon
|
|
|
|
}
|
|
|
|
nowPlayingInfo[MPMediaItemPropertyArtwork] = albumArt
|
|
|
|
}
|
|
|
|
|
2020-01-21 18:14:00 +00:00
|
|
|
mpInfoCenter.nowPlayingInfo = nowPlayingInfo
|
|
|
|
mpInfoCenter.playbackState = .playing
|
2020-02-15 15:13:37 +00:00
|
|
|
|
|
|
|
NotificationCenter.default.addObserver(
|
|
|
|
self,
|
|
|
|
selector: #selector(self.makeCurrent),
|
|
|
|
name: NSApplication.willBecomeActiveNotification,
|
|
|
|
object: nil
|
|
|
|
)
|
2019-11-30 15:08:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@objc func stop() {
|
2024-02-06 20:04:37 +00:00
|
|
|
for (cmd, _) in configs {
|
2019-11-30 15:08:32 +00:00
|
|
|
cmd.isEnabled = false
|
|
|
|
cmd.removeTarget(nil)
|
|
|
|
}
|
|
|
|
|
2020-01-21 18:14:00 +00:00
|
|
|
mpInfoCenter.nowPlayingInfo = nil
|
|
|
|
mpInfoCenter.playbackState = .unknown
|
|
|
|
}
|
|
|
|
|
2020-02-15 15:13:37 +00:00
|
|
|
@objc func makeCurrent(notification: NSNotification) {
|
2020-01-21 18:14:00 +00:00
|
|
|
mpInfoCenter.playbackState = .paused
|
|
|
|
mpInfoCenter.playbackState = .playing
|
|
|
|
updatePlaybackState()
|
|
|
|
}
|
|
|
|
|
|
|
|
func updatePlaybackState() {
|
|
|
|
mpInfoCenter.playbackState = isPaused ? .paused : .playing
|
2019-11-30 15:08:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func cmdHandler(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
|
2024-02-06 20:04:37 +00:00
|
|
|
guard let config = configs[event.command] else {
|
2019-11-30 15:08:32 +00:00
|
|
|
return .commandFailed
|
|
|
|
}
|
|
|
|
|
2024-02-06 20:04:37 +00:00
|
|
|
var state = config.state
|
|
|
|
if config.type == .repeatable {
|
2019-11-30 15:08:32 +00:00
|
|
|
state = MP_KEY_STATE_DOWN
|
2024-02-06 20:04:37 +00:00
|
|
|
configs[event.command]?.state = MP_KEY_STATE_DOWN
|
|
|
|
if config.state == MP_KEY_STATE_DOWN {
|
2019-11-30 15:08:32 +00:00
|
|
|
state = MP_KEY_STATE_UP
|
2024-02-06 20:04:37 +00:00
|
|
|
configs[event.command]?.state = MP_KEY_STATE_UP
|
2019-11-30 15:08:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-06 20:04:37 +00:00
|
|
|
EventsResponder.sharedInstance().handleMPKey(config.key, withMask: Int32(state))
|
2019-11-30 15:08:32 +00:00
|
|
|
|
|
|
|
return .success
|
|
|
|
}
|
2020-01-21 18:14:00 +00:00
|
|
|
|
|
|
|
@objc func processEvent(_ event: UnsafeMutablePointer<mpv_event>) {
|
|
|
|
switch event.pointee.event_id {
|
|
|
|
case MPV_EVENT_PROPERTY_CHANGE:
|
|
|
|
handlePropertyChange(event)
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func handlePropertyChange(_ event: UnsafeMutablePointer<mpv_event>) {
|
|
|
|
let pData = OpaquePointer(event.pointee.data)
|
|
|
|
guard let property = UnsafePointer<mpv_event_property>(pData)?.pointee else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch String(cString: property.name) {
|
|
|
|
case "pause" where property.format == MPV_FORMAT_FLAG:
|
|
|
|
isPaused = LibmpvHelper.mpvFlagToBool(property.data) ?? false
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|