My goal is to get the cover picture for the playlists in iPod library. And I did something like
playlistMediaItemCollections = MPMediaQuery.playlistsQuery().collections ?? []
let artworks = playlistMediaItemCollections.map { $0.valueForKey(MPMediaItemPropertyArtwork) as? MPMediaItemArtwork }
But it results in error
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MPConcreteMediaPlaylist 0x1468b1eb0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key artwork.'
Anyone knows how I can get the playlist artwork? Thanks
You should use valueForProperty instead:
$0.valueForProperty(MPMediaItemPropertyArtwork) as? MPMediaItemArtwork
However, I think unlike songs or albums, MediaPlayer API does not provide such property key that lets you retrieve the artwork of a playlist. You can check out possible ones that can be used with MPMediaPlaylist class:
let MPMediaPlaylistPropertyPersistentID: String
let MPMediaPlaylistPropertyName: String
let MPMediaPlaylistPropertyPlaylistAttributes: String
let MPMediaPlaylistPropertySeedItems: String
One alternative is, you can get artworks of songs in the playlist, and show either one of them or combine them to create a new artwork for the playlist.
I think Music app does the same thing like below if a playlist doesn't have an artwork image.
let playlist = MPMediaQuery.playlistsQuery().collections?.first
let artworks = playlist?.items.map { $0.valueForProperty(MPMediaItemPropertyArtwork) as? MPMediaItemArtwork }
Related
I try to get file list from the Music Library (iPod Music Library), but I can't do it, my list is always empty. I sure that I have tracks in Music Library, I check it in other app - and it works. But as I remember that application sent me a request to access the Music Library. Perhaps I also need to create such a request? Help me solve the problem. I use this code to get file list:
func fetchFileList() {
let mediaItems = MPMediaQuery.songs().items
let mediaCollection = MPMediaItemCollection(items: mediaItems ?? [])
print("mediaCollectionItems: \(mediaCollection.items)") //It's always empty
//Then I'd like to get url of the track
//let item = mediaCollection.items[0]
//let pathURL = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
//print("pathURL: \(pathURL)")
}
If you want to access the Music Library, you have to add NSAppleMusicUsageDescription key to your Info.plist with a description about what you want to do with the music.
Se apple documentation for more info: MediaPlayer Documentation
I understand how to enumerate images and video in the photo library using PHPhotoLibrary, but videos added through iTunes as "home videos", and visible only through the new TV app under the Home Video section, don't appear in that enumeration.
Is there another way of querying for them?
Well, it looks like the only other thing you can try to use are the Media Player API's.
https://developer.apple.com/documentation/mediaplayer/mpmediaquery
A query that specifies a set of media items from the device's media
library by way of a filter and a grouping type.
That might work, since I assume that your synced movies are part of the media library and not part of the photo library.
First you need to add the NSAppleMusicUsageDescription to your Info.plist. Then we can use MPMediaQuery and filter out the home video items.
let predicate: MPMediaPropertyPredicate = MPMediaPropertyPredicate(value: MPMediaType.homeVideo.rawValue, forProperty: MPMediaItemPropertyMediaType)
let query: MPMediaQuery = MPMediaQuery.init()
query.addFilterPredicate(predicate)
let items = query.items
for item : MPMediaItem in items!
{
// Here is your home video
}
See the MPMediaTypeHomeVideo item: https://developer.apple.com/documentation/mediaplayer/mpmediatype/mpmediatypehomevideo
Now items should contain all your home videos.
EDIT: I tested it, and it works for me on iOS 12, however title seems to be empty. You can access the video file itself using the assetURL property.
Here's an updated method.
import MediaPlayer
func video() {
MPMediaLibrary.requestAuthorization() { status in
if status == .authorized {
let predicate: MPMediaPropertyPredicate = MPMediaPropertyPredicate(value: MPMediaType.homeVideo.rawValue, forProperty: MPMediaItemPropertyMediaType)
let query: MPMediaQuery = MPMediaQuery.init()
query.addFilterPredicate(predicate)
if let items = query.items {
for item in items {
// Here is your home video
print(item.title ?? "title?")
}
}
}
}
}
you'll need to add this key to your info.plist
Privacy - Media Library Usage Description
I'm working on code that looks at the user's videos and passes them along to AVPlayer by way of an AVPlayerItem which takes a URL.
To get all the videos on an iOS device, you can do:
let videoNumber = MPMediaType.anyVideo.rawValue
let predicate = MPMediaPropertyPredicate.init(value: videoNumber, forProperty: MPMediaItemPropertyMediaType)
let query = MPMediaQuery.init()
query.addFilterPredicate(predicate)
if let items = query.items
{
mediaCollection = MPMediaItemCollection(items: items)
// -1 would indicate an error condition
print("number of items in collection is \(mediaCollection?.count ?? -1)")
}
When you select the MPMediaItem you want to use from the items array, there should be an assetURL to go with it.
Trouble is, on my device, all of my assetURL properties are NULL. Coincidentally, while hasProtectedAsset for each of these items is false, isCloudItem for each of these items is true.
How can I come up with a valid assetURL that I can pass along to any media player? It feels somewhat bogus that developers can't get proper references & access to media in a user's iPod library.
So I'm building some playlist and song retrieval into my app at the moment, and I'm really confused by some of the results I'm getting back from the API. It seems to be returning songs that no longer exist on Spotify or have been long removed from a playlist.
Retrieving a list of Playlists from a user is working fine, but just in case this problem is arising from the way I draw that playlist's tracks, here is the code I use to get them:
SPTPlaylistSnapshot.playlistWithURI(uri, accessToken: session.accessToken) { (error, playlistSnapshotOb) -> Void in
if let playlistSnapshot = playlistSnapshotOb as? SPTPlaylistSnapshot {
let itemz = playlistSnapshot.firstTrackPage.items //tracksForPlayback()
for item in itemz{
let track = item as! SPTPlaylistTrack
let splice = "\(track.uri)"
let trackURI = splice.stringByReplacingOccurrencesOfString("spotify:track:", withString: "")
var displayArtist = String()
let artistz = track.artists
if artistz.count > 1{
for i in 0...(artistz.count - 1){
let itz = artistz[i] as! SPTPartialArtist
if i > 0 {
displayArtist += ", \(itz.name)"
}else{
displayArtist += "\(itz.name)"
}
}
self.tracks.append(track.name)
self.ArtistObjects.append(displayArtist)
self.uriS.append(trackURI)
}else{
let singularArtist = artistz[0] as! SPTPartialArtist
displayArtist = singularArtist.name
self.tracks.append(track.name)
self.ArtistObjects.append(displayArtist)
self.uriS.append(trackURI)
}
Additionally, below is a screenshot of the desktop Spotify app showing the real content of the playlist I am pulling:
Spotify per Desktop
You'll see that the songs "Big Bank Dank" and "Light Day Remix" are not actually on this playlist, but for some reason, on my app below, when I pull this playlist, it has these songs listed:
Spotify In My App
(Apparently I can't post an actual image because of my rep - apologies)
Any idea why it's doing this?
The tracks are probably just not available any longer for some unspecified reason. This is quite common. By default, the Spotify client does not show unavailable tracks in playlists, but in settings there is a toggle you can flip so that they are shown as greyed out instead.
I don't know about iOS SDK, but there should be either an attribute telling you the available markets for the tracks or if it is playable or not, depending on the country of the user being logged in.
This is how it works in the Web API, which should be similar.
https://developer.spotify.com/web-api/track-relinking-guide/
I have tried the following:
let nowPlaying = MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo
However I get back nil everytime I run it with a song playing.
I would like to be able to grab the track title and artist and display it in my app.
You're going about this completely the wrong way. MPNowPlayingInfoCenter has nothing to do with learning what is currently playing. If you want to know what the Music app is currently playing, ask the "iPod music player" (in iOS 8, it is called MPMusicPlayerController.systemMusicPlayer).
try this, if you are writing an iOS app
let musicPlayer = MPMusicPlayerController.systemMusicPlayer
if let nowPlayingItem = musicPlayer.nowPlayingItem {
print(nowPlayingItem.title)
} else {
print("Nothing's playing")
}
This is a modified version of this answer.
Using Swift, you can get the Now Playing info, including title, artist, artwork and app on an iOS device using the following private API:
// 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 song info
MRMediaRemoteGetNowPlayingInfo(DispatchQueue.main, { (information) in
let bundleInfo = Dynamic._MRNowPlayingClientProtobuf.initWithData(information["kMRMediaRemoteNowPlayingInfoClientPropertiesData"])
print("\(information["kMRMediaRemoteNowPlayingInfoTitle"] as! String) by \(information["kMRMediaRemoteNowPlayingInfoArtist"] as! String) playing on \(bundleInfo.displayName.asString!)")
})
Returns SONG by ARTIST playing on APP.
Note this uses the Dynamic package to easily execute private headers.
This cannot be used in an App Store app due to the use of private API.
This is not an API to get the current playing item information from Music or another app, but to tell the system that your app is currently playing something and give it the information needed to display it on lock screen.
So basically what you're trying to do won't work as you expect it.
Did you set them?
var audioPlayer:MPMoviePlayerController=MPMoviePlayerController()
MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = [
MPMediaItemPropertyAlbumTitle: "Album Title",
MPMediaItemPropertyTitle: "Title",
MPNowPlayingInfoPropertyElapsedPlaybackTime: audioPlayer.currentPlaybackTime,
MPMediaItemPropertyPlaybackDuration: audioPlayer.duration]
The now playing info center supports the following media item property keys:
MPMediaItemPropertyAlbumTitle
MPMediaItemPropertyAlbumTrackCount
MPMediaItemPropertyAlbumTrackNumber
MPMediaItemPropertyArtist
MPMediaItemPropertyArtwork
MPMediaItemPropertyComposer
MPMediaItemPropertyDiscCount
MPMediaItemPropertyDiscNumber
MPMediaItemPropertyGenre
MPMediaItemPropertyPersistentID
MPMediaItemPropertyPlaybackDuration
MPMediaItemPropertyTitle