HLS on 3rd Party Devices

I'm new to channel development and have just written my first channel for Hockeystreams.com.

It works fine in PMC but doesn't work on anything else (Plex/Web, Roku, ATV). The files from HockeyStreams are m3u8.

 

I've gone thru the forums a number of times and I'm just not getting how/why the other devices can't play the streams. I'm under the impression that these devices can do HTTP Live Streams, which I've read, that's what the m3u files are. Also, from what I've read, the links are just passed to the device and they should take it from there.

 

So, I'm assuming I'm missing some basic understanding of what's really going on, and what needs to be done for this to work.

 

Could someone give me a quick 101 on what I'm missing, or at least point me to some docs/post that explain it.

 

Thanks a lot!

M

Yes, most clients are capable of handling HLS natively. As you said, PMS just hands off the url for the m3u8 playlist and the client handles playback from there on.

Your issues suggest that the clients are failing before they reach that point. Based on that, I have a couple trouble-shooting questions:

  1. Is your channel using an URL Service?
  2. If so, does your URL Service have a MetadataForURL() function?

I would guess no on using a URL service... 'cause I'm not 100% sure what a URL Service is.

This is the code that builds the menu (that I stole from somewhere);

@route('%s/livegames' % PREFIX)
def LiveGames():
    oc = ObjectContainer(title2='Live Games')
    url = HS_LIVEGAMESURL % TOKEN

    for video in GetLiveGames(url):
        (title, srcUrl) = video

        oc.add(VideoClipObject(
            key=Callback(GetStream, video_url=srcUrl, title=title),
            rating_key=0,
            title=title,
            items=[
                MediaObject(
                    parts=[PartObject(key=HTTPLiveStreamURL(url=srcUrl))]
                )
            ]
        ))

    return oc
 

and this is the GetStream code;

@route('%s/getstream' % PREFIX)
def GetStream(video_url, title):
    oc = ObjectContainer()

    oc.add(VideoClipObject(
        rating_key=video_url,
        title=title,
        items=[
            MediaObject(
                parts=[PartObject(key=HTTPLiveStreamURL(url=video_url))]
            )
        ]
    ))
    return oc
 

Thanks a lot for your help.

First thing that jumps out at me is that you have the rating_key set to 0 for every video in the for-loop. The rating key is supposed to be a unique identifier for the VideoObject (I sometimes use the url of the video itself when working without a URL Service). Try setting that to something unique for each video and see if that helps.

P.S. Here's a little reading on URL Services to whet your appetite.

I did have the rating_key set to the url, but it wasn't working and not throwing an error. I have successfully change all the rating_keys to the video's unique ID.

I did make a little headway on Plex/Web. I didn't have a url property in the GetStream function. I added one and now I get one step further to the screen with the Play button. But, when I click it I get 'This Channel is Currently Unavailable' in the browser and in the log it states 'No service found for URL http://...' and 'No matching services found for http://...'. So I'm assuming that's where the URL Services come into play.

Thanks for the link... I'll head that way now.

While I've got you on the line... Is there a way to refresh a menu automatically after, say, the Prefs are changed, or even refresh after a few minutes? I did see there used to be autoRefresh in the 1.0 framework, but haven't seen anything in 2.x.

Thanks again!

Passing the “url=” parameter will tell the framework to check for an URL Service. Using the key/rating_key combo is an alternative to using the url parameter.

It’s not a method I use often, so I’m not familiar enough with it to have the problem jump out at me from your code snippet. I did implement a similar method in the IMDb Trailers channel https://github.com/mikedm139/IMDb-Trailers.bundle/blob/master/Contents/Code/init.py
It doesn’t use HLS, but I don’t think that’s the actual issue here. Take a look for comparison. Particularly at the CreateTrailerObject() method and how it is called.

You should not always return an ObjectContainer in the Callback function. Here's another example of a channel that works without a URL Service: https://github.com/plexinc-plugins/Tagesschau.bundle/blob/master/Contents/Code/__init__.py

While I've got you on the line... Is there a way to refresh a menu automatically after, say, the Prefs are changed, or even refresh after a few minutes? I did see there used to be autoRefresh in the 1.0 framework, but haven't seen anything in 2.x.

autoRefresh is gone in the newer framework versions, but you can set the no_cache property (with value True) on an ObjectContainer:

oc = ObjectContainer(no_cache=True)

Cool - thanks for the examples guys. I'll check them out a little later and see if I can make some headway.

autoRefresh is gone in the newer framework versions, but you can set the no_cache property (with value True) on an ObjectContainer:

oc = ObjectContainer(no_cache=True)

So am I correct in thinking that you'd still have to back out to a higher menu and come back in to refresh a list of items when using no_cache=true?

So am I correct in thinking that you'd still have to back out to a higher menu and come back in to refresh a list of items when using no_cache=true?

No, clients _should_ refresh the ObjectContainer immediately.

No, clients _should_ refresh the ObjectContainer immediately.

Clients _do_ refresh the OC immediately, just like they should - thanks.

So I finally tracked down a Roku 2 to see what's up.

It appears that the Roku will crash Plex (on the Roku) if the url in PartObject(key=HTTPLiveStreamURL(url=url)) is empty (null or None).

So I'm guessing that's a bug in Plex on Roku?

Back to my main issue - I still can't get these videos to play in Plex/Web. I added an internal (?) service like sander1 explained.

def CreateVideoClipObject(game_id, url, title):

    vco = VideoClipObject(
        key=Callback(CreateVideoClipObject, game_id=game_id, url=url, title=title),
        rating_key=game_id,
        title=title,
        items=[
            MediaObject(
            parts = [
                PartObject(key=HTTPLiveStreamURL(url=url))
            ],
            container = Container.MP4,
            video_codec = VideoCodec.H264,
            video_resolution = ‘720’,
            audio_codec = AudioCodec.AAC,
            audio_channels = 2,
            optimized_for_streaming = True
            )
        ]
    )

    return vco

With this, I did get another step closer... but when I try to play it I get "Error loading player: No playable sources found".

So I finally tracked down a Roku 2 to see what's up.

It appears that the Roku will crash Plex (on the Roku) if the url in PartObject(key=HTTPLiveStreamURL(url=url)) is empty (null or None).

So I'm guessing that's a bug in Plex on Roku?

Why would you pass an empty URL to the player?

Why would you pass an empty URL to the player?

The HockeyStreams API doesn't return the url until the game starts.

Back to my main issue - I still can't get these videos to play in Plex/Web. I added an internal (?) service like sander1 explained.


def CreateVideoClipObject(game_id, url, title):

    vco = VideoClipObject(
        key=Callback(CreateVideoClipObject, game_id=game_id, url=url, title=title),
        rating_key=game_id,
        title=title,
        items=[
            MediaObject(
            parts = [
                PartObject(key=HTTPLiveStreamURL(url=url))
            ],
            container = Container.MP4,
            video_codec = VideoCodec.H264,
            video_resolution = ‘720’,
            audio_codec = AudioCodec.AAC,
            audio_channels = 2,
            optimized_for_streaming = True
            )
        ]
    )

    return vco

With this, I did get another step closer... but when I try to play it I get "Error loading player: No playable sources found".

Your missing the "include_container" arg and the handling. The client is likely failing because it perceives an infinite loop of ObjectContainers as it hits the key from the objectcontainer receives another objectcontainer etc...  Take another look at the examples that Sander and I provided.

Ahh - yes. For some reason I didn't think that was the needed in my situation.

I did figure out my problem.

It turns out that the token that is passed to the API during a GET could have slashes in it. Plex Media Center handled that fine, but Plex/Web, Roku and iOS didn't.

I've encoded the token and it works fine now.

And speaking of encoding - is there a slick way of encoding just the params in a url? Right now I'm using this;

    from urlparse import urlparse
    import urllib

    ## Encode the token param so it doesn’t fail on 3rd party devices

# Break apart the url

    parsed = urlparse(url)

# Encode the token param

    encodedToken = urllib.quote_plus(parsed[4])

# Revert the encoded equals sign back to =

    encodedToken = encodedToken.replace(’%3D’, ‘=’, 1)

# Put the url back together

    url = parsed[0] + ‘://’ + parsed[1] + parsed[2] + “?” + encodedToken
 

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.