如何在 Mac (Swift) 上获取当前正在播放的歌曲
How to get currently playing song on Mac (Swift)
在 Mac 上,触摸条可以自动检测正在播放音频的应用程序,并允许您 play/pause、跳过甚至搜索音频。这适用于 Spotify、Quicktime,甚至浏览器中的网站,例如 Google Chrome 上的 YouTube 标签。如何使用 Swift?
获取相同的信息(歌曲名称、缩略图、正在播放歌曲的应用程序、持续时间等)
macOS 与媒体播放器有更多的系统集成。例如,您可以向 Siri 询问当前歌曲或使用“今日播放”小部件:
所有这些集成都使用 Media Remote 私有框架中的私有 API。由于它是一个私有框架,如果您尝试在 Mac App Store 上提交它,Apple 将拒绝您的应用程序。您仍然可以对其进行公证,以便在 App Store 之外分发。
要使用该框架,您可以试试这个:
// Load framework
let bundle = CFBundleCreate(kCFAllocatorDefault, NSURL(fileURLWithPath: "/System/Library/PrivateFrameworks/MediaRemote.framework"))
// Get a Swift function for MRMediaRemoteGetNowPlayingInfo
guard let MRMediaRemoteGetNowPlayingInfoPointer = CFBundleGetFunctionPointerForName(bundle, "MRMediaRemoteGetNowPlayingInfo" as CFString) else { return }
typealias MRMediaRemoteGetNowPlayingInfoFunction = @convention(c) (DispatchQueue, @escaping ([String: Any]) -> Void) -> Void
let MRMediaRemoteGetNowPlayingInfo = unsafeBitCast(MRMediaRemoteGetNowPlayingInfoPointer, to: MRMediaRemoteGetNowPlayingInfoFunction.self)
// Get a Swift function for MRNowPlayingClientGetBundleIdentifier
guard let MRNowPlayingClientGetBundleIdentifierPointer = CFBundleGetFunctionPointerForName(bundle, "MRNowPlayingClientGetBundleIdentifier" as CFString) else { return }
typealias MRNowPlayingClientGetBundleIdentifierFunction = @convention(c) (AnyObject?) -> String
let MRNowPlayingClientGetBundleIdentifier = unsafeBitCast(MRNowPlayingClientGetBundleIdentifierPointer, to: MRNowPlayingClientGetBundleIdentifierFunction.self)
// Get song info
MRMediaRemoteGetNowPlayingInfo(DispatchQueue.main, { (information) in
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoArtist"] as! String)
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoTitle"] as! String)
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoAlbum"] as! String)
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoDuration"] as! String)
let artwork = NSImage(data: information["kMRMediaRemoteNowPlayingInfoArtworkData"] as! Data)
// Get bundle identifier
let _MRNowPlayingClientProtobuf: AnyClass? = NSClassFromString("_MRNowPlayingClientProtobuf")
let handle : UnsafeMutableRawPointer! = dlopen("/usr/lib/libobjc.A.dylib", RTLD_NOW)
let object = unsafeBitCast(dlsym(handle, "objc_msgSend"), to:(@convention(c)(AnyClass?,Selector?)->AnyObject).self)(_MRNowPlayingClientProtobuf,Selector("a"+"lloc"))
unsafeBitCast(dlsym(handle, "objc_msgSend"), to:(@convention(c)(AnyObject?,Selector?,Any?)->Void).self)(object,Selector("i"+"nitWithData:"),information["kMRMediaRemoteNowPlayingInfoClientPropertiesData"] as AnyObject?)
NSLog("%@", MRNowPlayingClientGetBundleIdentifier(object))
dlclose(handle)
})
在 Mac 上,触摸条可以自动检测正在播放音频的应用程序,并允许您 play/pause、跳过甚至搜索音频。这适用于 Spotify、Quicktime,甚至浏览器中的网站,例如 Google Chrome 上的 YouTube 标签。如何使用 Swift?
获取相同的信息(歌曲名称、缩略图、正在播放歌曲的应用程序、持续时间等)macOS 与媒体播放器有更多的系统集成。例如,您可以向 Siri 询问当前歌曲或使用“今日播放”小部件:
所有这些集成都使用 Media Remote 私有框架中的私有 API。由于它是一个私有框架,如果您尝试在 Mac App Store 上提交它,Apple 将拒绝您的应用程序。您仍然可以对其进行公证,以便在 App Store 之外分发。
要使用该框架,您可以试试这个:
// Load framework
let bundle = CFBundleCreate(kCFAllocatorDefault, NSURL(fileURLWithPath: "/System/Library/PrivateFrameworks/MediaRemote.framework"))
// Get a Swift function for MRMediaRemoteGetNowPlayingInfo
guard let MRMediaRemoteGetNowPlayingInfoPointer = CFBundleGetFunctionPointerForName(bundle, "MRMediaRemoteGetNowPlayingInfo" as CFString) else { return }
typealias MRMediaRemoteGetNowPlayingInfoFunction = @convention(c) (DispatchQueue, @escaping ([String: Any]) -> Void) -> Void
let MRMediaRemoteGetNowPlayingInfo = unsafeBitCast(MRMediaRemoteGetNowPlayingInfoPointer, to: MRMediaRemoteGetNowPlayingInfoFunction.self)
// Get a Swift function for MRNowPlayingClientGetBundleIdentifier
guard let MRNowPlayingClientGetBundleIdentifierPointer = CFBundleGetFunctionPointerForName(bundle, "MRNowPlayingClientGetBundleIdentifier" as CFString) else { return }
typealias MRNowPlayingClientGetBundleIdentifierFunction = @convention(c) (AnyObject?) -> String
let MRNowPlayingClientGetBundleIdentifier = unsafeBitCast(MRNowPlayingClientGetBundleIdentifierPointer, to: MRNowPlayingClientGetBundleIdentifierFunction.self)
// Get song info
MRMediaRemoteGetNowPlayingInfo(DispatchQueue.main, { (information) in
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoArtist"] as! String)
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoTitle"] as! String)
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoAlbum"] as! String)
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoDuration"] as! String)
let artwork = NSImage(data: information["kMRMediaRemoteNowPlayingInfoArtworkData"] as! Data)
// Get bundle identifier
let _MRNowPlayingClientProtobuf: AnyClass? = NSClassFromString("_MRNowPlayingClientProtobuf")
let handle : UnsafeMutableRawPointer! = dlopen("/usr/lib/libobjc.A.dylib", RTLD_NOW)
let object = unsafeBitCast(dlsym(handle, "objc_msgSend"), to:(@convention(c)(AnyClass?,Selector?)->AnyObject).self)(_MRNowPlayingClientProtobuf,Selector("a"+"lloc"))
unsafeBitCast(dlsym(handle, "objc_msgSend"), to:(@convention(c)(AnyObject?,Selector?,Any?)->Void).self)(object,Selector("i"+"nitWithData:"),information["kMRMediaRemoteNowPlayingInfoClientPropertiesData"] as AnyObject?)
NSLog("%@", MRNowPlayingClientGetBundleIdentifier(object))
dlclose(handle)
})