Search code examples
pythonlibvlc

Get the duration of a URL-based Media object with python-vlc - Cannot parse


I'm trying to use the python 2.7 python-vlc to parse then get the duration of a music track from a URL. Parsing doesn't work and playing then pausing the media returns -1 for the duration occasionally.

There are two ways I know of to parse media, which has to be done before using media.get_duration(). I can parse it, or I can play it.

No matter what, I cannot parse the media. Using parse_with_options() gives me parsed status MediaParsedStatus.skipped for everything except for parse_with_option(1,0)which gives me parsed status MediaParsedStatus.FIXME_(0L)

        p = vlc.MediaPlayer(songurl)
        media = p.get_media()
        media.parse_with_options(1, 0)
        print media.get_parsed_status()
        print media.get_duration()

The string "songurl" is the actual streaming URL of a song from Youtube or Google Play Music, which works perfectly fine with the MediaPlayer.

I have also tried playing the media for short 0.01 to 0.5 second periods then attempting to get the time, which works MOST OF THE TIME but randomly returns a duration of -1 about 1 in 10 times. Using media.get_duration() again returns the same result.

I would prefer to just parse the song rather than worry about playing it, but I can't figure out any way to parse it. I already submitted a bug report to the python-vlc github since I figure MediaParsedStatus.FIXME_(0L) is some sort of bug.

UPDATE: I GOT IT! This was possibly the biggest pain in all my programming career (which isnt much). Here's the code used to get the time for a URL track:

        instance = vlc.Instance()
        media = instance.media_new(songurl)
        player = instance.media_player_new()
        player.set_media(media)
        #Start the parser
        media.parse_with_options(1,0)
        while True:
            if str(media.get_parsed_status()) == 'MediaParsedStatus.done':
                break #Might be a good idea to add a failsafe in here.
        print media.get_duration()

Solution

  • media.parse_with_options is asynchronous. So your code isn't waiting for a response from URL, it's just immediately moving on. As with all asynchronous methods, you need to receive a notification that the data has been received and then you can move on. In this case it looks like it is the MediaParsedChanged event.

    https://www.videolan.org/developers/vlc/doc/doxygen/html/group__libvlc__media.html#ga55f5a33e22aa32e17a9bb75decd1497b

    Alternatively, you should be able to use the parse() method which is synchronous and will block until the meta data is received. This isn't recommended (and it's deprecated) because it could block indefinitely and lock up. But it is an option depending on what you are using the code for.

    https://www.videolan.org/developers/vlc/doc/doxygen/html/group__libvlc__media.html#ga4b71084fb35b3dd8cc6457a4d27baf0c

    EDIT:

    If you need an example of using the event manager with the python bindings, here is a great example: VLC Python EventManager callback type?

    Particularly, look at Rolf's answer as the way he is using it might be a good starting point for you.

    import vlc
    parseReady = 0
    
    def ParseReceived(event):
        global parseReady
        #set a flag that your data is ready
        parseReady = 1
    ...
    events = player.event_manager()
    events.event_attach(vlc.EventType.MediaParsedChanged, ParseReceived)
    ...
    parseReady = 0
    media.parse_with_options(1, 0)
    while parseReady == 0:
        #TODO: spin something to waste time
    
    #Once the flag is set, your data is ready
    print media.get_parsed_status()
    print media.get_duration()
    

    There are undoubtedly better ways to do it but that's a basic example. Note, according to the documentation, you can not call vlc methods from within an event callback. Thus the use of a simple flag rather that calling the media methods directly in the callback.