5

I want to display lyrics from song that is currently playing by iOS system player.

Here is my custom player:

import UIKit
import MediaPlayer
import AVFoundation

class NowPlayingController: NSObject {
    var musicPlayer: MPMusicPlayerController {
        if musicPlayer_Lazy == nil {
            musicPlayer_Lazy = MPMusicPlayerController.systemMusicPlayer()

            let center = NotificationCenter.default
            center.addObserver(self,
                selector: #selector(self.playingItemDidChange),
                name: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange,
                object: musicPlayer_Lazy)
            musicPlayer_Lazy!.beginGeneratingPlaybackNotifications()
        }

        return musicPlayer_Lazy!
    }
    private var musicPlayer_Lazy: MPMusicPlayerController?

    var nowPlaying: MPMediaItem?

    //If song changes
    func playingItemDidChange(notification: NSNotification) {
        nowPlaying = musicPlayer.nowPlayingItem
    }
}

To get lyrics from nowPlaying item I've tried 2 approaches and both of them always return nil.

This code always returns nil:

let lyricsText = nowPlaying?.value(forProperty: MPMediaItemPropertyLyrics) as? NSString as String?

In following code MPMediaItemPropertyAssetURL always returns nil instead of actual URL:

let songUrl = nowPlaying?.value(forProperty: MPMediaItemPropertyAssetURL) as? NSURL as URL?
if songUrl != nil {
    let songAsset = AVURLAsset(url: songUrl!, options: nil)
    lyricsText = songAsset.lyrics

All songs are on device (synced by iTunes), contain lyrics (displayed in system player) and non DRM-protected (ripped aac/mp3).

I'm testing this on real device: iPhone 6s/iOS 10.3

Any suggestions how I can get lyrics or why MPMediaItemPropertyAssetURL returns nil?

dandepeched
  • 424
  • 5
  • 20
  • These days, most iTunes content is either protected by FairPlay DRM or is Apple Music (also protected by FairPlay? Don't know.) & this type of content always returns a nil `MPMediaItemPropertyAssetURL`. You're only supposed to use it in a "jukebox" fashion, where iOS plays the music back for you. More sophisticated use cases are not possible. – Rhythmic Fistman Apr 05 '17 at 12:48
  • Did you try accessing like this below? `let lyric = nowPlaying?.lyrics let assetUrl = nowPlaying?.assetURL` also note that – Bluewings Apr 05 '17 at 15:52
  • @RhythmicFistman this is not the case, because my content is not an iTunes Store content. But I anyway have double checked with the following method which returns `false` (meaning there is no DRM on songs): `nowPlaying?.hasProtectedAsset` – dandepeched Apr 05 '17 at 15:53
  • @Bluewings I just tried it and both return `nill` – dandepeched Apr 05 '17 at 17:05

2 Answers2

2

I don't know why it was not working, but it looks like same code now works fine. Maybe it somehow connected to the singleton that I'm now using for player instance. Here is Swift 3 version that 100% working:

import UIKit
import MediaPlayer
import AVFoundation

class NowPlayingController: NSObject {

    static let sharedController = NowPlayingController()

    //MARK: Init
    private override init () {
        super.init()

        var musicPlayer_Lazy: MPMusicPlayerController?

        // System player instance
        if musicPlayer_Lazy == nil {
            musicPlayer_Lazy = MPMusicPlayerController.systemMusicPlayer()
            NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.playingItemDidChange),
                                               name: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange,
                                               object: musicPlayer_Lazy)
            musicPlayer_Lazy!.beginGeneratingPlaybackNotifications()
        }

        self.musicPlayer = musicPlayer_Lazy!
    }

    // MARK: Class properties
    var musicPlayer: MPMusicPlayerController!
    var nowPlaying: MPMediaItem?

    // MARK: Methods
    func playingItemDidChange(notification: NSNotification) {
        nowPlaying = musicPlayer.nowPlayingItem
        NotificationCenter.default.post(newSongNotification as Notification)
    }

    func getLyrics() {
        let songUrl = nowPlaying?.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
        let songAsset = AVURLAsset(url: songUrl!, options: nil)
        let lyricsText = songAsset.lyrics
    }
}
dandepeched
  • 424
  • 5
  • 20
1

I know this is kind of old, but I just spent about 4 hours researching how to read lyrics from an MP3 file, if it has lyrics. I am using AVAudioPlayer rather than MPMusicPlayerController. Holy Christ, it is so damn simple...

Swift 5.3 for macOS (I don't know if this works for iOS, but I imagine it would)

import AVFoundation

func getLyrics(url: URL) -> String? {
    let asset = AVAsset(url: url)
    return asset.lyrics
}

Notice that it is returning an optional, so it's best to check for nil. Hopefully this will save someone hours of research.

See Apple documentation here.

Yogesh Patel
  • 1,893
  • 1
  • 20
  • 55
SouthernYankee65
  • 1,129
  • 10
  • 22
  • Thanks for the info. However, I tried it for many songs in my iOS music player and the lyrics property was nil for every one of them. :-( – Carl Smith Dec 11 '20 at 11:26
  • I do not develop for iOS, but I find it odd that if the lyrics are embedded in your mp3 files (or other media types) that this does not work for you. The developer documentation at the link in the answer shows this method is valid for all iOS versions back to 4.0. – SouthernYankee65 Dec 13 '20 at 13:39
  • The big if there is whether or not the lyrics are embedded in the mp3 files. My only guess as to why it's not working is that they are not embedded. I find it odd (and sad) too. But I do appreciate the info! – Carl Smith Dec 13 '20 at 22:14
  • That would make sense. If they're not there then the return would be nil. To test that, import the mp3 into Music and add in some lyrics, then change the file url to point to the location where Music stores it, or sync it to the iOS device and test it, or test it with a file you know that has lyrics. Just a thought. – SouthernYankee65 Dec 15 '20 at 03:21
  • Exactly as you said! I added lyrics to a song in Music on my Mac and they showed up in my iOS app. Now all I have to do is add lyrics for all of the 570 songs in my favorite playlist - LOL! – Carl Smith Dec 15 '20 at 10:04