# Streaming works, except over iOS App

**URL:** <https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214>\
**Category:** Dev/API Corner\
**Tags:** plugin-dev\
**Created:** [November 29, 2013, 7:34am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214 "2013-11-29T07:34:38Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![realriot](https://avatars.discourse-cdn.com/v4/letter/r/919ad9/32.png) [@realriot](https://forums.plex.tv/u/realriot)\
**Post date:** [November 29, 2013, 7:34am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/1 "2013-11-29T07:34:38Z")

</div>

Hi,

&nbsp;

I've nearly finished my first Plex plugin...

&nbsp;

- Navigation through my menu works fine.
- Listing all video objects works fine, too.
- Accessing the video shows me the correct metadata too.
- Streaming the videos with browsers like firefox, chrome works.

&nbsp;

**But:**

Using the iOS App works fine, except playing the videos. The player appears but does not show the video...

My first thought was that the device cannot play the url directly. After calling the url directly on the device the iOS player was started and the video is displayed...

&nbsp;

I don't have a clue why the video cannot be played within the Plex iOS app. Perhaps someone has a hint for me whats going wrong and/or how I can debug this?

&nbsp;

I've found "extended logging" settings within the app. Where do all these logs go?

&nbsp;

Hope I can finish my plugin successfully...

Greets

Sascha

---

<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:** [November 29, 2013, 12:01pm UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/2 "2013-11-29T12:01:19Z")

</div>

I would start by checking the Plex Media Server.log for errors related to playback. It almost sounds like the iOS player is waiting for PMS to transcode the stream. Can you paste or link the playback-related portion of your plugin code?

---

<div class="post-metadata">

**Author:** ![realriot](https://avatars.discourse-cdn.com/v4/letter/r/919ad9/32.png) [@realriot](https://forums.plex.tv/u/realriot)\
**Post date:** [November 29, 2013, 2:01pm UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/3 "2013-11-29T14:01:48Z")

</div>

Of course..

Main handler:

```
def MainMenu():
  Plugin.AddViewGroup('InfoList', viewMode='InfoList', mediaType='items')

oc = ObjectContainer(title1=TEXT\_TITLE, view\_group = ‘InfoList’)

token = getToken(“test”)

# Only process if we got a valid token.

if token != “”:  
result = getChannels(token)  
if debug == True: Log("Token to use for api calls: " + token)

```
for channel in result['channels']['elements']:
  oc.add(createTVChannelObject(channel))

oc.add(PrefsObject(title=TEXT_PREFERENCES, thumb=R(ICON_SETTINGS)))

```

if len(oc) \< 1:  
failoc = ObjectContainer(header=“Fehler!”, message=“API Zugriff nicht möglich.”)  
failoc.add(PrefsObject(title=TEXT\_PREFERENCES, thumb=R(ICON\_SETTINGS)))  
return failoc

return oc
```

Videoobjects are created the following way:

```
def createTVChannelObject(channel, container = False):
  uniqid = channel['vodkatvChannelId']
  name = channel['name']
  thumb = channel['logo']
  url = channel['urls'][1].replace('http-interoud','http')
  summary = ""
  epg_title = ""
  epg_description = ""
  epg_start = ""
  epg_end = ""
  epg_length = 0

if channel[‘currentNext’].has\_key(‘current’):  
if channel[‘currentNext’][‘current’].has\_key(‘title’):  
epg\_title = channel[‘currentNext’][‘current’][‘title’]  
epg\_description = channel[‘currentNext’][‘current’][‘description’]  
epg\_start = datetime.datetime.fromtimestamp(channel[‘currentNext’][‘current’][‘start’]/1000).strftime(’%H:%M’)  
epg\_end = datetime.datetime.fromtimestamp(channel[‘currentNext’][‘current’][‘finish’]/1000).strftime(’%H:%M’)  
epg\_length = int(channel[‘currentNext’][‘current’][‘length’])

if container == False:  
summary = epg\_title  
else:  
summary = epg\_start + " - " + epg\_end + "

" + epg\_title +"

" + epg\_description

vco = VideoClipObject(  
key = Callback(createTVChannelObject, channel = channel, container = True),  
rating\_key = uniqid,  
title = name,  
summary = summary,  
duration = epg\_length,  
thumb = thumb,  
items = [  
MediaObject(  
video\_codec = VideoCodec.H264,  
audio\_codec = AudioCodec.AAC,  
audio\_channels = 2,  
optimized\_for\_streaming = True,  
parts = [PartObject(key = url)]  
)  
]  
)

if container:  
return ObjectContainer(objects = [vco])  
else:  
return vco  
return vco
```

Hope this helps...

---

<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:** [November 30, 2013, 4:15am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/4 "2013-11-30T04:15:52Z")

</div>

I suspect the issue is that the channel variable is not being serialized and passed properly between the initial call to createTVChannelObject() and the subsequent callback to the same function. Try adding a @route declaration, something like this:

```
@route(CHANNEL_PREFIX + '/createchannel', channel=dict)
def createTVChannelObject(channel, container = False):
...
```

Failing that, there are a few other possibilities that might make a difference; you could set the container type, or if the stream is HLS use the HTTPLiveStreamURL() to set the container and codec info for you like so:

```
MediaObject(
       parts = [PartObject(key = HTTPLiveStreamURL(url))]
     )
```

If using the HTTPLiveStreamURL() function or adding the proper container type (if not HLS) doesn't work, you might try returning the media url via a Callback. It shouldn't be necessary but is more commonly used than not in most channels and URL Services.

```
MediaObject(
       parts = [PartObject(key = Callback(PlayVideo, url=url))]
     )

@indirect  
def PlayVideo(url):  
return IndirectResponse(VideoClipObject, key=url)
```

---

<div class="post-metadata">

**Author:** ![realriot](https://avatars.discourse-cdn.com/v4/letter/r/919ad9/32.png) [@realriot](https://forums.plex.tv/u/realriot)\
**Post date:** [December 2, 2013, 8:18am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/5 "2013-12-02T08:18:52Z")

</div>

Thanks...I cannot get the point of "route"...

In your example you use "CHANNEL\_PREFIX" and "channel=dict". But cannot transport it to my needs and what this "route" command does. Examining the documentation of route didn't help me understand it.

Perhaps you can show me the right direction...

I've added the route:

```
@route('/video/tv/createchannel', channel=dict)
```

Within the plugin log I can see really long get requests...

> 2013-12-02 09:20:42,338 (7f03625e4700) :&nbsp; DEBUG (networking:172) - Requesting '[http://127.0.0.1:32400/:/plugins/com.plexapp.system/messaging/function/X1N0b3JlU2VydmljZTpJc0NoYW5uZWxCcm9rZW4\_/Y2VyZWFsMQoxCmxpc3QKMApyMAo\_/Y2VyZWFsMQoxCmRpY3QKMQpzMjUKY29tLnBsZXhhcHAucGx1Z2lucy5ld2V0dnMxMAppZGVudGlmaWVycjAK](http://127.0.0.1:32400/:/plugins/com.plexapp.system/messaging/function/X1N0b3JlU2VydmljZTpJc0NoYW5uZWxCcm9rZW4_/Y2VyZWFsMQoxCmxpc3QKMApyMAo_/Y2VyZWFsMQoxCmRpY3QKMQpzMjUKY29tLnBsZXhhcHAucGx1Z2lucy5ld2V0dnMxMAppZGVudGlmaWVycjAK)'  
> 2013-12-02 09:20:42,346 (7f03625e4700) :&nbsp; WARNING (objectkit:199) - Media part has no streams - attempting to synthesize  
> 2013-12-02 09:20:42,389 (7f03625e4700) :&nbsp; DEBUG (runtime:106) - Sending packed state data (106 bytes)  
> 2013-12-02 09:20:42,390 (7f03625e4700) :&nbsp; DEBUG (runtime:914) - Response: [200] MediaContainer, 216680 bytes  
> 2013-12-02 09:20:55,447 (7f03625e4700) :&nbsp; DEBUG (runtime:717) - Handling request GET /video/tv/createchannel?container=True&channel=%7B%22vodkatvChannelId%22%3A+%22int\_ticket\_daserste%22%2C+%22currentNext%22%3A+%7B%22current%22%3A+%7B%22finish%22%3A+1385974500000%2C+%22description%22%3A+%22Johanna+schw%5Cu00e4rmt+nicht+nur+f%5Cu00fcr+die+Weine+des+Burgenlandes%2C+sondern+besonders+f%5Cu00fcr+den+Winzer+Ferdi+Hofleitner.+Das+bemerkt+Thomas+und+vermutet%2C+dass+Johanna+einen+Weinladen+er%5Cu00f6ffnen+m%5Cu00f6chte%2C+doch+weit+gefehlt+...+S%5Cu00f6nke+hofft%2C+dass+Brian+ihm+nicht+die+Polizei+auf+den+Hals+hetzt%2C+nachdem+er+bei+Malakron+eingedrungen+ist.+Die+dort+entwendeten+Tabellen+geben+Jule+wenig+Aufschluss%2C+so+bittet+sie+Thomas%2C+sich+die+Unterlagen+anzusehen.+W%5Cu00e4hrend+in+Brian+leise+Zweifel+an+Ulis+Version+von+Jules+Suchtr%5Cu00fcckfall+wachsen.+Als+Rieke+sich+den+Fu%5Cu00df+verstaucht%2C+erweist+sich+Mick+als+wahrer+Gentleman%3A+Er+%5Cu00fcberl%5Cu00e4sst+den+Frauen+den+Wagen%2C+er+hingegen+eiert+mit+beiden+Fahrr%5Cu00e4dern+zur%5Cu00fcck.+Tine+hofft%2C+durch+das+Zusammenleben+mit+Lotte+ihrer+Tochter+wieder+n%5Cu00e4herzukommen. [...]

But streaming does not start...

The app still gives me **"An unknown problem occurred**".

Is there any chance to see the APP logfile?

Note: After changing to HTTPLiveStreamURL it behaviour has changed. Before nothing happend, now the error is displayed. It's a HLS stream delivered by Wowza Media Servers...

---

<div class="post-metadata">

**Author:** ![realriot](https://avatars.discourse-cdn.com/v4/letter/r/919ad9/32.png) [@realriot](https://forums.plex.tv/u/realriot)\
**Post date:** [December 2, 2013, 5:52pm UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/6 "2013-12-02T17:52:11Z")

</div>

Strange thing... I came back to home and tried to stream with the ios app again. And it works! But leaving my local network I cannot stream anymore. The unknown error is displayed...

The server where the stream comes from is hosted within the internet, not my local home. So I don't understand why I can stream at home but not when I'm somewhere else.

At home:

- Streaming via iOS app works

- Streaming via browser works

Mobile:

- Streaming via iOS app fails

- Streaming via browser works

Any suggestions for this really strange behaviour? :-)

Greets

Sascha

---

<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:** [December 2, 2013, 7:11pm UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/7 "2013-12-02T19:11:39Z")

</div>

Ah, that sounds like it might be an issue where the HLS server is rejecting the playback request (when mobile) because the IP of the player making the request is different than the IP (of the server) making the initial requests. Possibly due to IP tracking, or possibly due to cookie caching.

There is a "post\_url" parameter in the framework for handling IP-specific requests. The URL Service for the ABC channel is a good example of it's use. See [here](https://github.com/plexinc-plugins/Services.bundle/blob/master/Contents/Service%20Sets/com.plexapp.plugins.abc/URL/ABC/ServiceCode.pys#L116).

The other possibility is that the HLS Server requires some cookie(s) be returned with the playback request. The NBC URL service is a good example of how that can be implemented. See [here](https://github.com/plexinc-plugins/Services.bundle/blob/master/Contents/Service%20Sets/com.plexapp.plugins.nbc/URL/NBC/ServiceCode.pys#L107-L114).

The @route decorator for plugin functions is intended to provide a more RESTful interface and allowing casting variable in the appropriate type to avoid situations where they might be de-serialized incorrectly.

---

<div class="post-metadata">

**Author:** ![realriot](https://avatars.discourse-cdn.com/v4/letter/r/919ad9/32.png) [@realriot](https://forums.plex.tv/u/realriot)\
**Post date:** [December 3, 2013, 10:44am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/8 "2013-12-03T10:44:26Z")

</div>

Thanks, I'll try my luck with the post\_url argument. First of all I have to find out how to use it...

---

<div class="post-metadata">

**Author:** ![realriot](https://avatars.discourse-cdn.com/v4/letter/r/919ad9/32.png) [@realriot](https://forums.plex.tv/u/realriot)\
**Post date:** [December 4, 2013, 11:26am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/9 "2013-12-04T11:26:48Z")

</div>

Seems that the server may not fetch the token and do the login process... The client (browser, iOS app) should do this.

So every call to the api + streaming access should be done by the "client".

Is there a way to let the clients (browsers, ios apps, ..) do this and give the result back to the plex server that it can build the listings etc..?

---

<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:** [December 4, 2013, 1:24pm UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/10 "2013-12-04T13:24:54Z")

</div>

That is essentially the concept behind the post\_url parameter but on a smaller scale. When passing an arg to and ObjectContainer with the post\_url parameter, the client fetches the content of the URL and passes it back to PMS for manipulation. It might be possible to use the post\_url params in several places in your code to allow for clients fetching tokens etc. but I’m not aware of any examples where this has been done. You’re in somewhat uncharted territory in this regard.

---

<div class="post-metadata">

**Author:** ![realriot](https://avatars.discourse-cdn.com/v4/letter/r/919ad9/32.png) [@realriot](https://forums.plex.tv/u/realriot)\
**Post date:** [December 4, 2013, 1:48pm UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/11 "2013-12-04T13:48:11Z")

</div>

Thanks, I'll investigate this and come back to you with the result :-)

---

<div class="post-metadata">

**Author:** ![realriot](https://avatars.discourse-cdn.com/v4/letter/r/919ad9/32.png) [@realriot](https://forums.plex.tv/u/realriot)\
**Post date:** [December 4, 2013, 6:17pm UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/12 "2013-12-04T18:17:17Z")

</div>

Can you write an example procedure/function which shows how the "post\_url" works? I cannot figure out to make the client do post requests to the api with given username/password combinations and return the result to pms.

I know this is an undiscovered "country" within pms but perhaps with your deep understanding you'll have more success to write such a function.

---

<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:** [December 4, 2013, 8:46pm UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/13 "2013-12-04T20:46:11Z")

</div>

Ok. I'll see what I can come up with. Before I do, can you clarify the requirements for you situation?

- Do navigation elements for you plugin (return lists of videos, etc.) require being logged in? or is it possible to navigate without a login token and only playback requires a login?
- as far as we know, the token is IP specific rather than device specific?

---

<div class="post-metadata">

**Author:** ![realriot](https://avatars.discourse-cdn.com/v4/letter/r/919ad9/32.png) [@realriot](https://forums.plex.tv/u/realriot)\
**Post date:** [December 5, 2013, 9:03am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/14 "2013-12-05T09:03:17Z")

</div>

I'll try to describe the situation:

- There's a JSON api from which I need to get a token. To get the token I need to suply a username/password.
- After getting the token I can use it the fetch channellist, epg data, favorites etc. from the api. These data is delivered by json replies.
- If I go through the channelist reply I can find the URLs to the streams for the tv-channels which are delivered by hls streams. The token is added to the stream url.
- After a time of 5-15 minutes I have to renew the token.

As you can see there are only some contacts to an api which has to be done by the client. Finally the stream of the tv channels will be delivered to the client.

Greets

Sascha

---

<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:** [December 6, 2013, 3:21am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/15 "2013-12-06T03:21:44Z")

</div>

Would it be problematic to renew the token early? If not, I think the best implementation is likely to have the channel code login and use that token for building directories, navigation, etc. Then a separate login will occur (using a post\_url) when the client requests playback. I’ll try to put together an example if it sounds like that flow should work.

---

<div class="post-metadata">

**Author:** ![realriot](https://avatars.discourse-cdn.com/v4/letter/r/919ad9/32.png) [@realriot](https://forums.plex.tv/u/realriot)\
**Post date:** [December 6, 2013, 10:18am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/16 "2013-12-06T10:18:09Z")

</div>

The token can be renewed at every time.

---

<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:** [December 8, 2013, 12:11am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/17 "2013-12-08T00:11:18Z")

</div>

Based on your answers and pasted code above, I would recommend something like this:

```
def createTVChannelObject(channel, container = False):
  uniqid = channel['vodkatvChannelId']
  name = channel['name']
  thumb = channel['logo']
  url = channel['urls'][1].replace('http-interoud','http')
  summary = ""
  epg_title = ""
  epg_description = ""
  epg_start = ""
  epg_end = ""
  epg_length = 0

if channel[‘currentNext’].has\_key(‘current’):  
if channel[‘currentNext’][‘current’].has\_key(‘title’):  
epg\_title = channel[‘currentNext’][‘current’][‘title’]  
epg\_description = channel[‘currentNext’][‘current’][‘description’]  
epg\_start = datetime.datetime.fromtimestamp(channel[‘currentNext’][‘current’][‘start’]/1000).strftime(’%H:%M’)  
epg\_end = datetime.datetime.fromtimestamp(channel[‘currentNext’][‘current’][‘finish’]/1000).strftime(’%H:%M’)  
epg\_length = int(channel[‘currentNext’][‘current’][‘length’])

if container == False:  
summary = epg\_title  
else:  
summary = epg\_start + " - " + epg\_end + "

" + epg\_title +"

" + epg\_description

vco = VideoClipObject(  
key = Callback(createTVChannelObject, channel = channel, container = True),  
rating\_key = uniqid,  
title = name,  
summary = summary,  
duration = epg\_length,  
thumb = thumb,  
items = [  
MediaObject(  
parts = [PartObject(key = HTTPLiveStreamURL(Callback(PlayVideo, url=url, post\_url=LOGIN\_URL)))]  
)  
]  
)

if container:  
return ObjectContainer(objects = [vco])  
else:  
return vco  
return vco

@indirect  
def PlayVideo(url): # note post\_url is not included in the list of key\_word args  
‘’’ use LOGIN\_URL to execute login and get necessary token ‘’’  
final\_url = url + token # or however it needs to work  
return IndirectResponse(VideoClipObject, key=HTTPLiveStreamURL(final\_url))
```

---

<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:33am UTC](https://forums.plex.tv/t/streaming-works-except-over-ios-app/48214/18 "2019-12-21T00:33:07Z")

</div>

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