Flash player - how to extract video URL ... ?

I am rewriting my [unsupported] plugin ORF TVThek (Austrian TV on demand), that I had abandoned for a long time. In the meantime the web page changed drastically, especially in terms of how the play their videos. It looks to me they are using Flash now and I am having a really hard time finding the video URLs.

If somebody could please take a look at http://tvthek.orf.at/, select any video and and could give me some hints and tips of how I could get the video URLs, I would extremely appreciate it.

By using Firefox's Web Developer Inspector, I found some video URLs in an URL encoded Javascript object on that page, but would not have any idea how to extract that with the Plex Plug-In API.

I am new to the Plex Framework (at least V2.1) and Python, so please go slow on me ... ;)

Hi!

I looked at it briefly, and it looks promising. There are four different streaming methods to choose from but the most compatible ones would be the MP4 file or HLS(an rtsp stream and an rtmp stream also existed).

Just as you said above, the streams can be found in the Javascript code. Example website code I found:

'playlist': '[{\"id\":6879513,\"title\":\"Bombendrohung\",\"sources\":{\"0_android_Q1A\":{\"src\":\"rtsp:\/\/apasfw.apa.at:1935\/cms-worldwide\/mp4:2013-10-09_1900_tl_26_OBEROESTERREICH-HEUT_Bombendrohung__6875699__o__0000865893__s6879513___ng_OOEHiRes_19021600P_19034300P_Q1A.3gp\",\"quality_string\":\"niedrig\",\"quality_pattern\":\"Q1A\"},\"1_android_Q4A\":{\"src\":\"rtsp:\/\/apasfw.apa.at:1935\/cms-worldwide\/mp4:2013-10-09_1900_tl_26_OBEROESTERREICH-HEUT_Bombendrohung__6875699__o__0000865893__s6879513___ng_OOEHiRes_19021600P_19034300P_Q4A.mp4\",\"quality_string\":\"mittel\",\"quality_pattern\":\"Q4A\"},\"2_android_SMIL\":{\"src\":\"rtsp:\/\/apasfw.apa.at:1935\/cms-worldwide\/mp4:2013-10-09_1900_tl_26_OBEROESTERREICH-HEUT_Bombendrohung__6875699__o__0000865893__s6879513___ng_OOEHiRes_19021600P_19034300P_Q4A.mp4\",\"quality_string\":\"hoch\",\"quality_pattern\":\"SMIL\"},\"3_apple_Q1A\":{\"src\":\"http:\/\/apasfiis.apa.at\/ipad\/cms-worldwide\/2013-10-09_1900_tl_26_OBEROESTERREICH-HEUT_Bombendrohung__6875699__o__0000865893__s6879513___ng_OOEHiRes_19021600P_19034300P_Q1A.3gp\/playlist.m3u8\",\"quality_string\":\"niedrig\",\"quality_pattern\":\"Q1A\",\"type\":\"video\/3gpp; codecs=\\"avc1.42E01E, mp4a.40.2\\"\"},\"4_apple_Q4A\":{\"src\":\"http:\/\/apasfiis.apa.at\/ipad\/cms-worldwide\/2013-10-09_1900_tl_26_OBEROESTERREICH-HEUT_Bombendrohung__6875699__o__0000865893__s6879513___ng_OOEHiRes_19021600P_19034300P_Q4A.mp4\/playlist.m3u8\",\"quality_string\":\"mittel\",\"quality_pattern\":\"Q4A\",\"type\":\"video\/mp4; codecs=\\"avc1.42E01E, mp4a.40.2\\"\"},\"5_apple_SMIL\":{\"src\":\"http:\/\/apasfw.apa.at:1935\/cms-worldwide\/smil:2013-10-09_1900_tl_26_OBEROESTERREICH-HEUT_Bombendrohung__6875699__o__0000865893__s6879513___ng_OOEHiRes_19021600P_19034300P.smil\/playlist.m3u8\",\"quality_string\":\"hoch\",\"quality_pattern\":\"SMIL\",\"type\":\"video\/mp4; codecs=\\"avc1.42E01E, mp4a.40.2\\"\"}}}]'
    });

I would use a regular expression to find the playlist and then convert the resulting string into a JSON object which would then be used to get the video stream of choice

RE_PLAYLIST = Regex("\'playlist\': *\'(\[.*\])\'")

If you look at:

https://github.com/plexinc-plugins/Services.bundle/blob/master/Contents/Service%20Sets/com.plexapp.plugins.discovery/URL/Discovery/ServiceCode.pys

you can see how I did something similar(this one uses HLS)

I am new to the Plex Framework (at least V2.1) and Python, so please go slow on me ... ;)

Oops, I didn't see the above first :) .

If you need more help, I'll gladly help out tomorrow with more examples(code etc) for the specific website

@meo: Thank you very much, especially for the fast reply!

I guess I have to brush up my little bit of regex and JSON knowledge.

I looked at the playlist JSOn object and it seems to contain pretty much everything that is needed.

What I saw was that 'special characters' in the JSON file are escaped with \ - i.e. all quotes (") are \" and all forward slashes show up as \/.

Will the Plex APIs JSON object and it's function take care of that or will I have to fix that with a string function first?

What will I need to code in order to get the JSON playlist object of the URL and into a Python object? The playlist object contains an array of playlists, each identified by an id, a title and an array of sources. How can I navigate these JSON attributes, once they are in a Python [dict ... ?] object?

Thanks and Best Regards,

alex

Oops, I didn't see the above first :) .

If you need more help, I'll gladly help out tomorrow with more examples(code etc) for the specific website

Thanks meo!

I am looking forward to the code examples.

All the URLs in the playlist JSON object are RTSP streams and according to this post they are not supported in Plex.

If this is really true I found some RTMP streams hidden in an URLencoded XML string (created in another Javascript (ORF.flashXML).

I did not get the RTMP streams working with the Plex player yet, though ...

Cheers,

alex

If you are able to grab that JSON data fully in a way that you can use the Plex JSON parsing API, using JSONObjectform (see http://dev.plexapp.com/docs/api/parsekit.html#module-JSON) when you call the data items, it will remove the extra backslashes.

If for some reason that method gives you errors and instead you have to use regex to pull certain parts of the JSON out as raw data, then you will have remove those "\" from it manually.

Thanks shopgirl284!

Next thing unclear to me - do I need to develop this as an URL Service or shall I just do it in my __init__ code.

I follow some parallel discussions about URL Services here on this forum, but this is still not clear to me ...

Are URL Services mandatory or do I get away without one?

At the moment I really do not see the advantages of having one

Thanks and Cheers,

alex

URL Services are not mandatory but they are quite beneficial. They may seem like extra work and it takes a bit to wrap one’s head around their use, but it’s worth it.
As a general rule, any time you can browse to a webpage with a unique URL to view a specific video… use a URL Service.
If your plugin is accessing one long list of links which point directly to media files… a URL Service won’t really work for you.
The plugin framework is geared towards URL Services. Because of that, plugins which don’t use URL Services when they should, end up being much more complicated they otherwise need to be.
In your case, I would definitely handle parsing the JSON from the video page in a URL service. Then you only need the plugin to handle navigation.

Thanks meo!

I am looking forward to the code examples.

All the URLs in the playlist JSON object are RTSP streams and according to this post they are not supported in Plex.

If this is really true I found some RTMP streams hidden in an URLencoded XML string (created in another Javascript (ORF.flashXML).

I did not get the RTMP streams working with the Plex player yet, though ...

Cheers,

alex

Hmmm... I tried a couple of more pages, like this one:

http://tvthek.orf.at/programs/972117-Wetter-ZIB-20/episodes/6867833-Wetter

and they all contained HLS streams.

If you are able to grab that JSON data fully in a way that you can use the Plex JSON parsing API, using JSONObjectform (see http://dev.plexapp.com/docs/api/parsekit.html#module-JSON) when you call the data items, it will remove the extra backslashes.

If for some reason that method gives you errors and instead you have to use regex to pull certain parts of the JSON out as raw data, then you will have remove those "\" from it manually.

It seems that the Plex Framework can't handle these escaped strings, I tried the following code(ServiceCode.pys in URL service)

RE_PLAYLIST = Regex("\'playlist\': *\'\[(.*)\]\'")

####################################################################################################
def MetadataObjectForURL(url):
# FILL IN
pass

####################################################################################################
def MediaObjectsForURL(url):
return [
MediaObject(
audio_channels = 2,
optimized_for_streaming = True,
parts = [
PartObject(
key = HTTPLiveStreamURL(Callback(PlayVideo, url = url))
)
]
)
]

####################################################################################################
def PlayVideo(url):
content = HTTP.Request(url).content

playListString = RE_PLAYLIST.search(content).groups()[0]
playList = JSON.ObjectFromString(playListString)

return Redirect(playList['sources']['5_apple_SMIL'])

and the JSON.ObjectFromString throws an exception ...

But you should now be able to see the concept at least, zwalex

Backslash is a special character in regex. It’s used to escape the following character. In order for your rexex to match you need to escape the backslashes (with backslashes).

Backslash is a special character in regex. It's used to escape the following character. In order for your rexex to match you need to escape the backslashes (with backslashes).

You'll also need to escape other regex special characters in your match string, such as * [ and ]
The result can often look quite backslash-y
Eg. \\\* should match \*

All those slashes  :)

Here is how the playlistString looks like:

{\"id\":6880245,\"title\":\"Wetter\",\"sources\":{\"0_android_Q1A\":{\"src\":\"rtsp:\/\/apasfw.apa.at:1935\/cms-worldwide\/mp4:2013-10-09_1954_sd_01_WETTER_____6867833__o__0000865959__s6880245___er_ORF1HiRes_19543207P_19554506P_Q1A.3gp\",\"quality_string\":\"niedrig\",\"quality_pattern\":\"Q1A\"},\"1_android_Q4A\":{\"src\":\"rtsp:\/\/apasfw.apa.at:1935\/cms-worldwide\/mp4:2013-10-09_1954_sd_01_WETTER_____6867833__o__0000865959__s6880245___er_ORF1HiRes_19543207P_19554506P_Q4A.mp4\",\"quality_string\":\"mittel\",\"quality_pattern\":\"Q4A\"},\"2_android_SMIL\":{\"src\":\"rtsp:\/\/apasfw.apa.at:1935\/cms-worldwide\/mp4:2013-10-09_1954_sd_01_WETTER_____6867833__o__0000865959__s6880245___er_ORF1HiRes_19543207P_19554506P_Q4A.mp4\",\"quality_string\":\"hoch\",\"quality_pattern\":\"SMIL\"},\"3_apple_Q1A\":{\"src\":\"http:\/\/apasfiis.apa.at\/ipad\/cms-worldwide\/2013-10-09_1954_sd_01_WETTER_____6867833__o__0000865959__s6880245___er_ORF1HiRes_19543207P_19554506P_Q1A.3gp\/playlist.m3u8\",\"quality_string\":\"niedrig\",\"quality_pattern\":\"Q1A\",\"type\":\"video\/3gpp; codecs=\\"avc1.42E01E, mp4a.40.2\\"\"},\"4_apple_Q4A\":{\"src\":\"http:\/\/apasfiis.apa.at\/ipad\/cms-worldwide\/2013-10-09_1954_sd_01_WETTER_____6867833__o__0000865959__s6880245___er_ORF1HiRes_19543207P_19554506P_Q4A.mp4\/playlist.m3u8\",\"quality_string\":\"mittel\",\"quality_pattern\":\"Q4A\",\"type\":\"video\/mp4; codecs=\\"avc1.42E01E, mp4a.40.2\\"\"},\"5_apple_SMIL\":{\"src\":\"http:\/\/apasfw.apa.at:1935\/cms-worldwide\/smil:2013-10-09_1954_sd_01_WETTER_____6867833__o__0000865959__s6880245___er_ORF1HiRes_19543207P_19554506P.smil\/playlist.m3u8\",\"quality_string\":\"hoch\",\"quality_pattern\":\"SMIL\",\"type\":\"video\/mp4; codecs=\\"avc1.42E01E, mp4a.40.2\\"\"}}}

it has escaped " inside a " ":

\"type\":\"video\/mp4; codecs=\\"avc1.42E01E, mp4a.40.2\\"\"

This seemed to do the trick:

####################################################################################################
def PlayVideo(url):
    content = HTTP.Request(url).content
playListString = RE_PLAYLIST.search(content).groups()[0].replace('\\\"', "'")
playList = JSON.ObjectFromString(playListString)

return Redirect(playList['sources']['4_apple_Q4A']['src'])

@zwalex:

- The above example shows a very simple part of a URL service, i.e. ServiceCode.pys. You will also need ServiceInfo.plist(containing regex for which the URL service shall work with)

Example code for __init.py__ (which I used for some basic testing):

TITLE  = 'OrfTVThek'
PREFIX = '/video/orftvthek'

BASE_URL = ‘http://tvthek.orf.at

ART = “art-default.jpg”
THUMB = ‘icon-default.png’

###################################################################################################
def Start():
DirectoryObject.thumb = R(THUMB)
ObjectContainer.art = R(ART)

HTTP.CacheTime = CACHE_1HOUR  

###################################################################################################
@handler(PREFIX, TITLE, thumb = THUMB, art = ART)
def MainMenu():
oc = ObjectContainer(title1 = TITLE)

oc.add(
    EpisodeObject(
        url = 'http://tvthek.orf.at/programs/972117-Wetter-ZIB-20/episodes/6867833-Wetter',
        title = "TEST"
    )
)

return oc

Thank you so much meo!

I created a ServiceInfo.plist file in my plu-in bundle Content/Services directory. It looks like this:

<?xml version="1.0" encoding="UTF-8"?>



	URL
	
		ORF TVThek
		
			TestURLs
			
				http://tvthek.orf.at/programs/1379-Sport-Bild/episodes/6855679-Sport-Bild
				http://tvthek.orf.at/topics/Fu%C3%9Fball/6886675-Sport-Aktuell/segments/6886697-OeFB-Team-in-Schweden				
			
		<key>URLPatterns</key>
		<array>
			<string>^http://tvthek.orf.at/(programs|topics).*?</string>
		</array>
	</dict>
</dict>

In the Content/Services/URL/ORF TVThek/ directory in my bundle I created a ServiceCode.pys file that looks like this:

RE_PLAYLIST = Regex("\'playlist\': *\'\[(.*)\]\'")

####################################################################################################
def MetadataObjectForURL(url):
# FILL IN
pass

####################################################################################################
def MediaObjectsForURL(url):
return [
MediaObject(
audio_channels = 2,
optimized_for_streaming = True,
parts = [
PartObject(
key = HTTPLiveStreamURL(Callback(PlayVideo, url = url))
)
]
)
]

####################################################################################################
def PlayVideo(url):
content = HTTP.Request(url).content

playListString = RE_PLAYLIST.search(content).groups()[0].replace('\\\"', '"')
playList = JSON.ObjectFromString(playListString)

return Redirect(playList['sources']['4_apple_Q4A']['src'])

it's essentially a cut and paste of meo's code above - I just changed the "'" to '"' in the .replace to get a valid JSON object, which then looks like this (for one of the test URLs):

{
   "id":6892023,
   "title":"Otto Waalkes im Anflug",
   "sources":{
      "0_android_Q1A":{
         "src":"rtsp:\/\/apasfw.apa.at:1935\/cms-worldwide\/mp4:2013-10-11_1730_tl_02_heute-leben_Otto-Waalkes-im-Anfl__6891999__o__0000868857__s6892023___g__ORF2HiRes_18135616P_18191000P_Q1A.3gp",
         "quality_string":"niedrig",
         "quality_pattern":"Q1A"
      },
      "1_android_Q4A":{
         "src":"rtsp:\/\/apasfw.apa.at:1935\/cms-worldwide\/mp4:2013-10-11_1730_tl_02_heute-leben_Otto-Waalkes-im-Anfl__6891999__o__0000868857__s6892023___g__ORF2HiRes_18135616P_18191000P_Q4A.mp4",
         "quality_string":"mittel",
         "quality_pattern":"Q4A"
      },
      "2_android_SMIL":{
         "src":"rtsp:\/\/apasfw.apa.at:1935\/cms-worldwide\/mp4:2013-10-11_1730_tl_02_heute-leben_Otto-Waalkes-im-Anfl__6891999__o__0000868857__s6892023___g__ORF2HiRes_18135616P_18191000P_Q4A.mp4",
         "quality_string":"hoch",
         "quality_pattern":"SMIL"
      },
      "3_apple_Q1A":{
         "src":"http:\/\/apasfiis.apa.at\/ipad\/cms-worldwide\/2013-10-11_1730_tl_02_heute-leben_Otto-Waalkes-im-Anfl__6891999__o__0000868857__s6892023___g__ORF2HiRes_18135616P_18191000P_Q1A.3gp\/playlist.m3u8",
         "quality_string":"niedrig",
         "quality_pattern":"Q1A",
         "type":"video\/3gpp; codecs=\"avc1.42E01E, mp4a.40.2\""
      },
      "4_apple_Q4A":{
         "src":"http:\/\/apasfiis.apa.at\/ipad\/cms-worldwide\/2013-10-11_1730_tl_02_heute-leben_Otto-Waalkes-im-Anfl__6891999__o__0000868857__s6892023___g__ORF2HiRes_18135616P_18191000P_Q4A.mp4\/playlist.m3u8",
         "quality_string":"mittel",
         "quality_pattern":"Q4A",
         "type":"video\/mp4; codecs=\"avc1.42E01E, mp4a.40.2\""
      },
      "5_apple_SMIL":{
         "src":"http:\/\/apasfw.apa.at:1935\/cms-worldwide\/smil:2013-10-11_1730_tl_02_heute-leben_Otto-Waalkes-im-Anfl__6891999__o__0000868857__s6892023___g__ORF2HiRes_18135616P_18191000P.smil\/playlist.m3u8",
         "quality_string":"hoch",
         "quality_pattern":"SMIL",
         "type":"video\/mp4; codecs=\"avc1.42E01E, mp4a.40.2\""
      }
   }
}

When I try to run one of my test files against the Plex URL Services lookup (http://localhost:32400/system/services/url/lookup?url=) it returns a blank screen, and in the com.plexapp.system.log, I see:

2013-10-11 12:04:08,870 (-4fa6b000) :  DEBUG (runtime:717) - Handling request GET /system/services/url/lookup?url=http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug
2013-10-11 12:04:08,938 (-4fa6b000) :  DEBUG (runtime:814) - Found route matching /system/services/url/lookup
2013-10-11 12:04:08,938 (-4fa6b000) :  DEBUG (services:23) - Looking up URL 'http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug'
2013-10-11 12:04:08,939 (-4fa6b000) :  DEBUG (services:615) - Found a service matching 'http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug' - ORF TVThek (com.plexapp.plugins.orftvthek)
2013-10-11 12:04:08,940 (-4fa6b000) :  DEBUG (services:41) - Loading service code for ORF TVThek (URLServiceRecord)
2013-10-11 12:04:08,951 (-4fa6b000) :  DEBUG (networking:172) - Requesting 'http://resources-cdn.plexapp.com/hashes.json'
2013-10-11 12:04:10,293 (-4fa6b000) :  DEBUG (services:41) - Loading service code for Fallback (URLServiceRecord)
2013-10-11 12:04:10,524 (-4fa6b000) :  DEBUG (services:615) - Found a service matching 'http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug' - ORF TVThek (com.plexapp.plugins.orftvthek)
2013-10-11 12:04:10,526 (-4fa6b000) :  DEBUG (networking:233) - Fetching HTTP headers for 'http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug'
2013-10-11 12:04:11,883 (-4fa6b000) :  INFO (ServiceCode:86) - Length: 107496  Type: text/html
2013-10-11 12:04:11,884 (-4fa6b000) :  DEBUG (networking:172) - Requesting 'http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug'
2013-10-11 12:04:14,871 (-4fa6b000) :  DEBUG (services:602) - No service found for URL 'ipad:http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug'
2013-10-11 12:04:14,871 (-4fa6b000) :  DEBUG (services:617) - No matching services found for 'ipad:http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug'
2013-10-11 12:04:14,873 (-4fa6b000) :  DEBUG (services:602) - No service found for URL 'ipad:http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug'
2013-10-11 12:04:14,873 (-4fa6b000) :  DEBUG (services:617) - No matching services found for 'ipad:http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug'
2013-10-11 12:04:14,875 (-4fa6b000) :  DEBUG (networking:233) - Fetching HTTP headers for 'http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug'
2013-10-11 12:04:15,353 (-4fa6b000) :  INFO (ServiceCode:86) - Length: 107484  Type: text/html
2013-10-11 12:04:15,354 (-4fa6b000) :  DEBUG (networking:172) - Requesting 'http://tvthek.orf.at/topics/Unterhaltung/6891999-heute-leben/segments/6892023-Otto-Waalkes-im-Anflug'
2013-10-11 12:04:18,514 (-4fa6b000) :  DEBUG (runtime:914) - Response: [404] NoneType, 0 bytes

What am I missing ... ?

Thanks in advance,

alex

OK, got a few steps further.

I changed my ServiceCode.pys to return some mockup meta data

...
def MetadataObjectForURL(url):
        return VideoClipObject(
                title = "The title goes here ...",
                summary = "Here's where the summary will go ...",
                # thumb = thumb,
        )
...

Her's now what I get from the URL services lookup:


    
        
            
                
                
            
        
    

The file attribute in the Part node is empty - do I need to be concerned about this?

Thanks in advance,

alex

I added code to my plug-in to return a VideoClipObject:

@route(PREFIX + '/videoitems')
def VideoItems(title,date,dur,summary,thumb,url):
	Log.Debug("Entering VideoItems")
video = VideoClipObject(
	title = title,
	summary = summary,
	# originally_available_at = date,
	url = URL + url
	)
return video

On the plug-in's log it looks like it's executing my URL service, but that it did not find a stream:

2013-10-11 14:50:18,052 (-4faed000) :  DEBUG (runtime:814) - Found route matching /video/orftvthek/videoitems
2013-10-11 14:50:18,052 (-4faed000) :  DEBUG (__init__:217) - Entering VideoItems
2013-10-11 14:50:18,054 (-4faed000) :  DEBUG (services:615) - Found a service matching 'http://tvthek.orf.at/topics/Nationalratswahl%202013/6878971-Zeit-im-Bild/segments/6879107-Studio-Gespraech-mit-Hans-Buerger' - ORF TVThek (com.plexapp.plugins.orftvthek)
2013-10-11 14:50:18,054 (-4faed000) :  DEBUG (services:41) - Loading service code for ORF TVThek (URLServiceRecord)
2013-10-11 14:50:18,070 (-4faed000) :  DEBUG (services:41) - Loading service code for Fallback (URLServiceRecord)
2013-10-11 14:50:18,254 (-4faed000) :  DEBUG (services:615) - Found a service matching 'http://tvthek.orf.at/topics/Nationalratswahl%202013/6878971-Zeit-im-Bild/segments/6879107-Studio-Gespraech-mit-Hans-Buerger' - ORF TVThek (com.plexapp.plugins.orftvthek)
2013-10-11 14:50:18,256 (-4faed000) :  DEBUG (services:615) - Found a service matching 'http://tvthek.orf.at/topics/Nationalratswahl%202013/6878971-Zeit-im-Bild/segments/6879107-Studio-Gespraech-mit-Hans-Buerger' - ORF TVThek (com.plexapp.plugins.orftvthek)
2013-10-11 14:50:18,257 (-4faed000) :  WARNING (objectkit:199) - Media part has no streams - attempting to synthesize
2013-10-11 14:50:18,259 (-4faed000) :  DEBUG (runtime:914) - Response: [200] VideoClipObject, 1501 bytes

So obviously I am missing something in my URL Service.

Any hints and idea appreciated.

Thanks,

alex

OK, got a few steps further.

I changed my ServiceCode.pys to return some mockup meta data

...
def MetadataObjectForURL(url):
        return VideoClipObject(
                title = "The title goes here ...",
                summary = "Here's where the summary will go ...",
                # thumb = thumb,
        )
...

Her's now what I get from the URL services lookup:

    

The file attribute in the Part node is empty - do I need to be concerned about this?

Thanks in advance,

alex

I don't think this should be a problem.

I added code to my plug-in to return a VideoClipObject:

@route(PREFIX + '/videoitems')
def VideoItems(title,date,dur,summary,thumb,url):
	Log.Debug("Entering VideoItems")
video = VideoClipObject(
	title = title,
	summary = summary,
	# originally_available_at = date,
	url = URL + url
	)
return video

On the plug-in's log it looks like it's executing my URL service, but that it did not find a stream:

2013-10-11 14:50:18,052 (-4faed000) :  DEBUG (runtime:814) - Found route matching /video/orftvthek/videoitems
2013-10-11 14:50:18,052 (-4faed000) :  DEBUG (__init__:217) - Entering VideoItems
2013-10-11 14:50:18,054 (-4faed000) :  DEBUG (services:615) - Found a service matching 'http://tvthek.orf.at/topics/Nationalratswahl%202013/6878971-Zeit-im-Bild/segments/6879107-Studio-Gespraech-mit-Hans-Buerger' - ORF TVThek (com.plexapp.plugins.orftvthek)
2013-10-11 14:50:18,054 (-4faed000) :  DEBUG (services:41) - Loading service code for ORF TVThek (URLServiceRecord)
2013-10-11 14:50:18,070 (-4faed000) :  DEBUG (services:41) - Loading service code for Fallback (URLServiceRecord)
2013-10-11 14:50:18,254 (-4faed000) :  DEBUG (services:615) - Found a service matching 'http://tvthek.orf.at/topics/Nationalratswahl%202013/6878971-Zeit-im-Bild/segments/6879107-Studio-Gespraech-mit-Hans-Buerger' - ORF TVThek (com.plexapp.plugins.orftvthek)
2013-10-11 14:50:18,256 (-4faed000) :  DEBUG (services:615) - Found a service matching 'http://tvthek.orf.at/topics/Nationalratswahl%202013/6878971-Zeit-im-Bild/segments/6879107-Studio-Gespraech-mit-Hans-Buerger' - ORF TVThek (com.plexapp.plugins.orftvthek)
2013-10-11 14:50:18,257 (-4faed000) :  WARNING (objectkit:199) - Media part has no streams - attempting to synthesize
2013-10-11 14:50:18,259 (-4faed000) :  DEBUG (runtime:914) - Response: [200] VideoClipObject, 1501 bytes

So obviously I am missing something in my URL Service.

Any hints and idea appreciated.

Thanks,

alex

The warning "Media part has no streams - attempting to synthesize" is not something to worry about. The framework will create/synthesize two streams(audio + video) automatically for you if you explicitely don't set them(most of the plugins do not do this).

Which client are you using? Try PMC first since that client will play almost anything

You should be able to put the part key after your local host (http://localhost:32400) and paste that address in your web browser and the resulting web page will start playing the video since you use Redirect in your PlayVideo. So, based on the XML file you show your URL service test returning above, that address would be the http address shown below. Entering that in your web browser will either play the video which means your URL service works, or show you an XML document that includes any errors.

http://localhost:32400/:/plugins/com.plexapp.system/serviceFunction/url/com.plexapp.plugins.orftvthek/ORF%20TVThek/PlayVideo.m3u8?args=Y2VyZWFsMQoxCnR1cGxlCjAKcjAK&kwargs=Y2VyZWFsMQoxCmRpY3QKMQpzMTAwCmh0dHA6Ly90dnRoZWsub3JmLmF0L3RvcGljcy9VbnRlcmhhbHR1bmcvNjg5MTk5OS1oZXV0ZS1sZWJlbi9zZWdtZW50cy82ODkyMDIzLU90dG8tV2FhbGtlcy1pbS1BbmZsdWdzMwp1cmxyMAo_

I added code to my plug-in to return a VideoClipObject:

@route(PREFIX + '/videoitems')
def VideoItems(title,date,dur,summary,thumb,url):
	Log.Debug("Entering VideoItems")
video = VideoClipObject(
	title = title,
	summary = summary,
	# originally_available_at = date,
	url = URL + url
	)
return video

Oh, I didn't see it first, you need to add the VideoClipObject to an ObjectContainer like this:

@route(PREFIX + '/videoitems')
def VideoItems(title,date,dur,summary,thumb,url):
	Log.Debug("Entering VideoItems")
    oc = ObjectContainer()
  
video = VideoClipObject(
	title = title,
	summary = summary,
	# originally_available_at = date,
	url = URL + url
	)

    oc.add(video)

return oc