Displaying Artwork for .MP3 file
Asked Answered
J

3

13

I am trying to currently display the album artwork for a locally stored .MP3 track in an ImageView. Does anyone know how to fetch this artwork in Swift in order to accomplish this?

I have found this solution (iOS AVFoundation: How do I fetch artwork from an mp3 file?) but the code is written in Objective C. I simply want to grab the image embedded in my MP3 and display it in my ImageView.

I've looked at the API documentation for the MPMediaItemArtwork and found an example that also accomplishes what I am trying to accomplish in Objective C as well here(http://www.codeitive.com/0zHjkUjUWX/not-able-to-get-the-uiimage-from-mpmediaitempropertyartwork.html) but cannot come up with a solution. My code is as follows:

import UIKit
import AVFoundation
import MediaPlayer

class ViewController: UIViewController {
let audioPath:NSURL! = NSBundle.mainBundle().URLForResource("SippinOnFire", withExtension: "mp3")

@IBOutlet var artistImage: UIImageView!
@IBOutlet var trackLabel: UILabel!
@IBOutlet var artistLabel: UILabel!
@IBOutlet var sliderValue: UISlider!
var player:AVAudioPlayer = AVAudioPlayer()


@IBAction func play(sender: AnyObject) {


    let audioInfo = MPNowPlayingInfoCenter.defaultCenter()
println(audioInfo)


    player.play()
    //println("Playing \(audioPath)")


    let playerItem = AVPlayerItem(URL: audioPath)
    let metadataList = playerItem.asset.metadata as! [AVMetadataItem]


    for item in metadataList {
        if let stringValue = item.value {
           println(item.commonKey)
            if item.commonKey == "title" {
                trackLabel.text = stringValue as? String
            }
            if item.commonKey  == "artist" {
                artistLabel.text = stringValue as? String
            }
            if item.commonKey  == "artwork" {
                if let audioImage = UIImage(data: item.value as! NSData) {
                    let audioArtwork = MPMediaItemArtwork(image: audioImage)
                    println(audioImage.description)
                }
           }


        }
    }
}
@IBAction func pause(sender: AnyObject) {

    player.pause()
}
@IBAction func stop(sender: AnyObject) {

    player.stop()
    player.currentTime = 0;
}
@IBAction func sliderChanged(sender: AnyObject) {

    player.volume = sliderValue.value

}

override func viewDidLoad() {
    super.viewDidLoad()

             var error:NSError? = nil

    player = AVAudioPlayer(contentsOfURL: audioPath!, error: &error)

    player.volume = 0.5;

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


}

Here is a screen shot of my sample .mp3 file. As you can see there is indeed album artwork that is both visible in the "get info" section of Finder. I've also opened the .mp3 in my iTunes to make sure and have confirmed there is artwork in the "get info" section of it there as well as under the "artwork" tab.

However, when trying to use the commonKey to assign the image to my imageView I find that there is no commonKey for "artwork".

sample MP3 data

Thanks

Jessejessee answered 14/5, 2015 at 17:31 Comment(0)
S
14

Change your snippet of code into this (I already tested it):

I added println lines commented in places of interest, Feel free to uncomment in order to see what is happening.

   for item in metadataList {
        if item.commonKey == nil{
            continue
        }

        if let key = item.commonKey, let value = item.value {
            //println(key)
            //println(value)
            if key == "title" {
                trackLabel.text = value as? String
            }
            if key  == "artist" {
                artistLabel.text = value as? String
            }
            if key == "artwork" {
                if let audioImage = UIImage(data: value as! NSData) {
                  //println(audioImage.description)
                    artistImage.image = audioImage
                }
            }
        }
    }

UPDATE: A bit of clean up of this code

for item in metadataList {

    guard let key = item.commonKey, let value = item.value else{
        continue
    }

   switch key {
    case "title" : trackLabel.text = value as? String
    case "artist": artistLabel.text = value as? String
    case "artwork" where value is NSData : artistImage.image = UIImage(data: value as! NSData)
    default:
      continue
   }
}

UPDATE: For Swift 4

for item in metadataList {

    guard let key = item.commonKey?.rawValue, let value = item.value else{
        continue
    }

   switch key {
    case "title" : trackLabel.text = value as? String
    case "artist": artistLabel.text = value as? String
    case "artwork" where value is Data : artistImage.image = UIImage(data: value as! Data)
    default:
      continue
   }
}
Silvey answered 14/5, 2015 at 19:42 Comment(2)
Perfect! This was the solution that worked for me! Thanks for your help. So was it correct to use .metadata instead of the .commonmetadata? Is one just more in depth coverage of metadata than the other? Kudos and cheers!Jessejessee
Here is the up to date syntax with Swift 4 : linkConstabulary
M
5

edit/update Swift 4 or later:

import MediaPlayer


var nowPlayingInfo: [String: Any] = [:]
let playerItem = AVPlayerItem(url: url)
let metadataList = playerItem.asset.metadata

for item in metadataList {
    switch item.commonKey {
    case .commonKeyTitle?:
        nowPlayingInfo[MPMediaItemPropertyTitle] = item.stringValue ?? ""
    case .commonKeyType?:
        nowPlayingInfo[MPMediaItemPropertyGenre] = item.stringValue ?? ""
    case .commonKeyAlbumName?:
        nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = item.stringValue ?? ""
    case .commonKeyArtist?:
        nowPlayingInfo[MPMediaItemPropertyArtist] = item.stringValue ?? ""
    case .commonKeyArtwork?:
        if let data = item.dataValue,
            let image = UIImage(data: data) {
            nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { _ in image }
        }
    case .none: break
    default: break
    }
}

let audioInfo = MPNowPlayingInfoCenter.default()
audioInfo.nowPlayingInfo = nowPlayingInfo

Note: You will have to invoke beginReceivingRemoteControlEvents() otherwise it will not work on the actual device. You will also need to set your app Background Modes (Audio and AirPlay) and set your AVAudioSession category to AVAudioSessionCategoryPlayback and set it active:

do {
    try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers, .allowAirPlay])
    print("Playback OK")
    try AVAudioSession.sharedInstance().setActive(true)
    print("Session is Active")
} catch {
    print(error)
}

enter image description here

Midden answered 14/5, 2015 at 18:39 Comment(6)
I tried the code above previous already, however there is no commonKey for "artwork" listed so the image must be storing elsewhere in the file. When I println my commonKey items all that displays is [title type copyrights albumName artist] If I "get info" on the file I can clearly see artwork for the mp3 in both the Preview section as well as artwork under the "artwork" tab in iTunes. However, there is no "artwork" commonKey that can be used to capture this.Jessejessee
It is extracting the info from the file, your song probably doesn't have any artwork on it. Make sure your MP3 file has an embedded artworkMidden
I've updated my question to include a screen shot of the tracks info. As you can see there is artwork embedded in the .mp3. I didnt take a second screen shot of the "artwork" tab in iTunes, but the artwork also appears there as well.Jessejessee
Can you post a link of the file so I can test it here?Midden
Updated my question..I edited my code to use playerItem.asset.metadata instead of playerItem.asset.commonmetadata (which I'm assuming is similar, just contains more detailed metadata of the file?) and I am now able to see "artwork" as a commonKey available. However I am getting an error: (fatal error: unexpectedly found nil while unwrapping an Optional value) when running. I'm not sure if I am closer to the solution Leonardo was suggesting by using metadata instead, but I certainly see more extracted fields using this.Jessejessee
What about track duration ? How can I change current time with slider in Lock screen or control center ?Islander
S
0

Try this:

It appears that sometimes iOS 8 returns nil at first attempt of obtaining this info:

if let audioCenter = MPNowPlayingInfoCenter.defaultCenter(){
        if let audioInfo = audioCenter.nowPlayingInfo{
            if let artwork = audioInfo[MPMediaItemPropertyArtwork] as? MPMediaItemArtwork
            {
                var image: UIImage? = artwork.imageWithSize(artwork.bounds.size)

                if image == nil {
                    image = artwork.imageWithSize(artwork.bounds.size);
                }

                if image != nil{
                    println("image loaded")
                }
            }
        }
    }
Silvey answered 14/5, 2015 at 19:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.