# HLS on 3rd Party Devices

**URL:** <https://forums.plex.tv/t/hls-on-3rd-party-devices/43173>\
**Category:** Dev/API Corner\
**Tags:** plugin-dev\
**Created:** [September 21, 2013, 7:17pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173 "2013-09-21T19:17:23Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![FredenFromSweden](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/fredenfromsweden/32/7934_2.png) [@FredenFromSweden](https://forums.plex.tv/u/FredenFromSweden)\
**Post date:** [September 21, 2013, 7:17pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/1 "2013-09-21T19:17:23Z")

</div>

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.

&nbsp;

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&nbsp;should&nbsp;take it from there.

&nbsp;

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.

&nbsp;

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

&nbsp;

Thanks a lot!

M

---

<div class="post-metadata">

**Author:** ![mikedm139](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/mikedm139/32/96909_2.png) [@mikedm139](https://forums.plex.tv/u/mikedm139)\
**Post date:** [September 21, 2013, 8:10pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/2 "2013-09-21T20:10:17Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![FredenFromSweden](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/fredenfromsweden/32/7934_2.png) [@FredenFromSweden](https://forums.plex.tv/u/FredenFromSweden)\
**Post date:** [September 21, 2013, 10:05pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/3 "2013-09-21T22:05:08Z")

</div>

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

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

&nbsp; &nbsp; &nbsp; &nbsp; oc.add(VideoClipObject(  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; key=Callback(GetStream, video\_url=srcUrl, title=title),  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rating\_key=0,  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title=title,  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; items=[  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MediaObject(  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parts=[PartObject(key=HTTPLiveStreamURL(url=srcUrl))]  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ]  
&nbsp; &nbsp; &nbsp; &nbsp; ))

&nbsp; &nbsp; return oc  
&nbsp;
```

and this is the GetStream code;

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

&nbsp; &nbsp; oc.add(VideoClipObject(  
&nbsp; &nbsp; &nbsp; &nbsp; rating\_key=video\_url,  
&nbsp; &nbsp; &nbsp; &nbsp; title=title,  
&nbsp; &nbsp; &nbsp; &nbsp; items=[  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MediaObject(  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parts=[PartObject(key=HTTPLiveStreamURL(url=video\_url))]  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )  
&nbsp; &nbsp; &nbsp; &nbsp; ]  
&nbsp; &nbsp; ))  
&nbsp; &nbsp; return oc  
&nbsp;
```

Thanks a lot for your help.

---

<div class="post-metadata">

**Author:** ![mikedm139](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/mikedm139/32/96909_2.png) [@mikedm139](https://forums.plex.tv/u/mikedm139)\
**Post date:** [September 21, 2013, 11:10pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/4 "2013-09-21T23:10:51Z")

</div>

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](http://devblog.plexapp.com/2012/05/11/the-power-of-the-url-service/)&nbsp;to whet your appetite.

---

<div class="post-metadata">

**Author:** ![FredenFromSweden](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/fredenfromsweden/32/7934_2.png) [@FredenFromSweden](https://forums.plex.tv/u/FredenFromSweden)\
**Post date:** [September 23, 2013, 12:31am UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/5 "2013-09-23T00:31:49Z")

</div>

I did have the&nbsp;rating\_key set to the url, but it wasn't working and not throwing an error. I have&nbsp;successfully&nbsp;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&nbsp;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&nbsp;Services&nbsp;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&nbsp;haven't&nbsp;seen anything in 2.x.

Thanks again!

---

<div class="post-metadata">

**Author:** ![mikedm139](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/mikedm139/32/96909_2.png) [@mikedm139](https://forums.plex.tv/u/mikedm139)\
**Post date:** [September 23, 2013, 4:53pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/6 "2013-09-23T16:53:06Z")

</div>

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/](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.

---

<div class="post-metadata">

**Author:** ![sander1](https://avatars.discourse-cdn.com/v4/letter/s/4bbf92/32.png) [@sander1](https://forums.plex.tv/u/sander1)\
**Post date:** [September 23, 2013, 6:03pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/7 "2013-09-23T18:03:47Z")

</div>

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](https://github.com/plexinc-plugins/Tagesschau.bundle/blob/master/Contents/Code/ __init__.py)

---

<div class="post-metadata">

**Author:** ![sander1](https://avatars.discourse-cdn.com/v4/letter/s/4bbf92/32.png) [@sander1](https://forums.plex.tv/u/sander1)\
**Post date:** [September 23, 2013, 6:10pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/8 "2013-09-23T18:10:44Z")

</div>

> 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&nbsp;haven't&nbsp;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)
```

---

<div class="post-metadata">

**Author:** ![FredenFromSweden](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/fredenfromsweden/32/7934_2.png) [@FredenFromSweden](https://forums.plex.tv/u/FredenFromSweden)\
**Post date:** [September 23, 2013, 7:23pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/9 "2013-09-23T19:23:14Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![sander1](https://avatars.discourse-cdn.com/v4/letter/s/4bbf92/32.png) [@sander1](https://forums.plex.tv/u/sander1)\
**Post date:** [September 24, 2013, 2:34am UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/10 "2013-09-24T02:34:33Z")

</div>

> 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.

---

<div class="post-metadata">

**Author:** ![FredenFromSweden](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/fredenfromsweden/32/7934_2.png) [@FredenFromSweden](https://forums.plex.tv/u/FredenFromSweden)\
**Post date:** [September 25, 2013, 2:05pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/11 "2013-09-25T14:05:27Z")

</div>

> No, clients \_should\_ refresh the ObjectContainer immediately.

Clients \_do\_ refresh the OC immediately, just like they should - thanks.

---

<div class="post-metadata">

**Author:** ![FredenFromSweden](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/fredenfromsweden/32/7934_2.png) [@FredenFromSweden](https://forums.plex.tv/u/FredenFromSweden)\
**Post date:** [September 27, 2013, 2:58am UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/12 "2013-09-27T02:58:04Z")

</div>

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

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

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

---

<div class="post-metadata">

**Author:** ![FredenFromSweden](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/fredenfromsweden/32/7934_2.png) [@FredenFromSweden](https://forums.plex.tv/u/FredenFromSweden)\
**Post date:** [September 27, 2013, 3:04am UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/13 "2013-09-27T03:04:11Z")

</div>

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):

&nbsp; &nbsp; vco = VideoClipObject(  
&nbsp; &nbsp; &nbsp; &nbsp; key=Callback(CreateVideoClipObject, game\_id=game\_id, url=url, title=title),  
&nbsp; &nbsp; &nbsp; &nbsp; rating\_key=game\_id,  
&nbsp; &nbsp; &nbsp; &nbsp; title=title,  
&nbsp; &nbsp; &nbsp; &nbsp; items=[  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MediaObject(  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parts = [  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PartObject(key=HTTPLiveStreamURL(url=url))  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ],  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; container = Container.MP4,  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; video\_codec = VideoCodec.H264,  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; video\_resolution = ‘720’,  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; audio\_codec = AudioCodec.AAC,  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; audio\_channels = 2,  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; optimized\_for\_streaming = True  
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )  
&nbsp; &nbsp; &nbsp; &nbsp; ]  
&nbsp; &nbsp; )

&nbsp; &nbsp; 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".

---

<div class="post-metadata">

**Author:** ![mikedm139](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/mikedm139/32/96909_2.png) [@mikedm139](https://forums.plex.tv/u/mikedm139)\
**Post date:** [September 27, 2013, 3:11am UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/14 "2013-09-27T03:11:13Z")

</div>

> 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?

---

<div class="post-metadata">

**Author:** ![FredenFromSweden](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/fredenfromsweden/32/7934_2.png) [@FredenFromSweden](https://forums.plex.tv/u/FredenFromSweden)\
**Post date:** [September 27, 2013, 3:34am UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/15 "2013-09-27T03:34:30Z")

</div>

> Why would you pass an empty URL to the player?

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

---

<div class="post-metadata">

**Author:** ![mikedm139](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/mikedm139/32/96909_2.png) [@mikedm139](https://forums.plex.tv/u/mikedm139)\
**Post date:** [September 27, 2013, 2:55pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/16 "2013-09-27T14:55:54Z")

</div>

> 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):
> 
> &nbsp; &nbsp; vco = VideoClipObject(  
> &nbsp; &nbsp; &nbsp; &nbsp; key=Callback(CreateVideoClipObject, game\_id=game\_id, url=url, title=title),  
> &nbsp; &nbsp; &nbsp; &nbsp; rating\_key=game\_id,  
> &nbsp; &nbsp; &nbsp; &nbsp; title=title,  
> &nbsp; &nbsp; &nbsp; &nbsp; items=[  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MediaObject(  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parts = [  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PartObject(key=HTTPLiveStreamURL(url=url))  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ],  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; container = Container.MP4,  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; video\_codec = VideoCodec.H264,  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; video\_resolution = ‘720’,  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; audio\_codec = AudioCodec.AAC,  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; audio\_channels = 2,  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; optimized\_for\_streaming = True  
> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )  
> &nbsp; &nbsp; &nbsp; &nbsp; ]  
> &nbsp; &nbsp; )
> 
> &nbsp; &nbsp; 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... &nbsp;Take another look at the examples that Sander and I provided.

---

<div class="post-metadata">

**Author:** ![FredenFromSweden](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/fredenfromsweden/32/7934_2.png) [@FredenFromSweden](https://forums.plex.tv/u/FredenFromSweden)\
**Post date:** [September 28, 2013, 1:01pm UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/17 "2013-09-28T13:01:34Z")

</div>

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

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

```
# Break apart the url

```

&nbsp; &nbsp; parsed = urlparse(url)

```
# Encode the token param

```

&nbsp; &nbsp; encodedToken = urllib.quote\_plus(parsed[4])

```
# Revert the encoded equals sign back to =

```

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

```
# Put the url back together

```

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

---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/plex/original/3X/2/a/2acb9765406f63293d357b4ec509ec39aa28f2ad.png) [@system](https://forums.plex.tv/u/system)\
**Post date:** [December 21, 2019, 12:07am UTC](https://forums.plex.tv/t/hls-on-3rd-party-devices/43173/18 "2019-12-21T00:07:53Z")

</div>

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