Hints for first plugin

I’m planning to make my first plex plugin. I’ve got some experience with java, html, C, C++ and some scripting and HDL languages. However, I’ve never written in python. I’ve taken a look at some examples and the plex development manual. Everything seems understandable, but writing my own plugin is a bit harder to do.



I’ve started with the beginning of the python script. I’ve created a small list, with an icon and a background. But I would like to get some suggestions about how I should continue.

I’d like to integrate the video’s of this site:

http://www.deredactie.be/cm/vrtnieuws/mediatheek

Maybe I could do something with the “permalink”?



I also found out that the different tabs are accessible with these links:

http://www.deredactie.be/cm/vrtnieuws/mediatheek/weer

http://www.deredactie.be/cm/vrtnieuws/mediatheek/ookdatnog

http://www.deredactie.be/cm/vrtnieuws/mediatheek/meestbekeken





Does anyone know an example of a similar plugin? Or suggestions on how I should continue with this?

Hey flubr, welcome!





I don’t really know of a specific plugin that you should look at. But I can tell you that the tools here are your friends:

http://wiki.plexapp.com/index.php/Scraping_helper_tools



That, and there are several people in the forums that will be more that happy to assist/guide you.



Personally, I got my start by looking at the MTV Music Videos plugin. But since you probably can’t access that one, I’m not sure.

And, FWIW, I only had elementary Pascal and C++ experience prior to writing plugins.

Thanks for your help, the tools are very useful.

I’m trying to base my plugin upon the CNET TV plugin.

However I’m getting some trouble with the xpath. I’m making step by step small adaptations to the CNET design, but at a certain point, the plugin suddenly doesn’t show up in Plex anymore.



if I change this line:

for item in HTML.ElementFromURL(ROOT_URL).xpath(’//ul[@id=“mainMenu”]/li’):



into this line (with ROOT_URL =“http://www.deredactie.be/cm/vrtnieuws/mediatheek”):

for item in HTML.ElementFromURL(ROOT_URL).xpath(’.//*[@id=‘mediaTabs’]’):



than plex doesn’t show the plugin anymore.

Anyone an idea about what I’m doing wrong?

The


'.//*[@id='mediaTabs']'

has two issues. First, you need to use double quotes around mediaTabs since the whole thing is inside single quotes. i.e.


'.//*[@id="mediaTabs"]' 

and second, I’m not sure the * is valid xpath syntax. What are you trying to extract?




Thanks for your help. The double quotes solved the problem.

However I'm still struggeling with the site structure.
http://www.deredactie.be/cm/vrtnieuws/mediatheek

The site consists of two navigation levels, so I'd like to create a menu and u submenu.
The xpath is built like this for the first navigation level:
//DIV[@id="tabs"]/DIV[@id="navL1"]/DIV[@id="mediaTabs"]/DIV[@id="mediaTab2"]/DIV/A[@id="nav_2_3791"]
For the second navigation level, the xpath is built like this:
//DIV[@id="tabs"]/DIV[@id="navL2"]/DIV[@id="mediaSubTabs"]/DIV[@id="subnav_2_3790"]/DIV[@id="mediaSubTab_2_3790_4"]/DIV/A[@id="nav_2_14128"]

I can use this xpath:
//DIV[@id="tabs"]/*/*/*/*
but it gives me a 1-level list.
Because the navigation level (navL1 or navL2) is in the middle of the xpath, I don't know how to divide the results in two levels (a menu and a submenu).
Anyone a suggestion?
Thanks in advance!

Not sure if the ids are fixed our not, they don’t look it. So how about



<br />
//div[@id="mediaTab2"]//a<br />




for the first level and

<br />
//div[@id="mediaSubTabs"]//a<br />




for the second?



Ok, thanks. I'll try to implement it later.
I decided to start simple, with only one menu. When this works, I can expand and integrate more functions (such as a second menu).

I have already written some code. I thought it would work, but unfortunately it doesn't.
The plugin starts in Plex, I can choose an item from the menu, but when I click on this item, I get an empty list (instead of a list of videos).
I've been searching and trying to fix this issue, but I don't see what I'm doing wrong.
I also don't know if I should work with a site configuration or not, and if this could be the cause of the empty menus.

I based my code upon the CNET plugin, the MTV2 plugin and the "fromsport" plugin.

This is the code I've written so far:

<br />
import datetime, re, pickle<br />
<br />
PLUGIN_PREFIX   = "/video/deredactie"<br />
ROOT_URL        = "http://www.deredactie.be/cm/vrtnieuws/mediatheek"<br />
WEER            = "http://www.deredactie.be/cm/vrtnieuws/mediatheek/weer"<br />
OOKDATNOG		= "http://www.deredactie.be/cm/vrtnieuws/mediatheek/ookdatnog"<br />
LIVESTREAM		= "http://www.deredactie.be/cm/vrtnieuws/mediatheek/livestream"<br />
MEESTBEKEKEN	= "http://www.deredactie.be/cm/vrtnieuws/mediatheek/meestbekeken"<br />
####################################################################################################<br />
def Start():<br />
  Plugin.AddPrefixHandler(PLUGIN_PREFIX, MainMenu, "De Redactie", "icon-default.png", "art-default.png")<br />
  Plugin.AddViewGroup("Details", viewMode="InfoList", mediaType="items")<br />
  MediaContainer.art = R('art-default.png')<br />
  MediaContainer.title1 ="De Redactie"<br />
  DirectoryItem.thumb=R("icon-default.png")<br />
  <br />
<br />
#####################################  <br />
def MainMenu():<br />
    dir = MediaContainer() <br />
    dir.Append(Function(DirectoryItem(Videos, title='Weer'), pageUrl = WEER))<br />
    dir.Append(Function(DirectoryItem(Videos, title='Ook dat nog'), pageUrl = OOKDATNOG))<br />
    dir.Append(Function(DirectoryItem(Videos, title='Livestream'), pageUrl = LIVESTREAM))<br />
    dir.Append(Function(DirectoryItem(Videos, title='Meest bekeken'), pageUrl = MEESTBEKEKEN))<br />
    return dir<br />
<br />
#####################################<br />
def Videos(sender, pageUrl):<br />
    dir = MediaContainer(title2=sender.itemTitle)<br />
    content = XML.ElementFromURL(pageUrl, True)<br />
    for video in content.xpath('//div[@id="defaultList"]/div[1]/div/div[1]/div'):<br />
        link = video.xpath("span")[0].get('href')<br />
        image = video.xpath("img[1]")[0].get('src')<br />
        title = video.xpath("span/a")[0].text<br />
        dir.Append(WebVideoItem(link, title=title, thumb=image))<br />
    return dir<br />
<br />
#####################################<br />


if you are getting the top level menu (Weer etc) but no content then the issue is here:



<br />
for video in content.xpath('//div[@id="defaultList"]/div[1]/div/div[1]/div'):<br />
        link = video.xpath("span")[0].get('href')<br />
        image = video.xpath("img[1]")[0].get('src')<br />
        title = video.xpath("span/a")[0].text<br />
        dir.Append(WebVideoItem(link, title=title, thumb=image))<br />




two potential issues. First, the xpath //div[@id="defaultList"]/div[1]/div/div[1]/div does not return anything so there's no loop to go around. Second, one of the inner xpath is wrong or returning nothing and then an exception would be thrown, which will be in your log file.

As for site config, you'll need one if you use WebVideoItem. But, depending on the format of the link URL, you may not need to. If it's a URL to a page with a flash player on it, you'll need a site config. If it's a URL to the actual video media then use VideoItem instead and no site config. Post an example of the link URL and I can help you further.

Jonny

Thanks Jonny. I tested my xpaths with some firefox tools, and they seem to give me the information I need.The screenshots show the information and an URL to the video.

This is what I get in the log:


[/font]<br />
Feb 13, 2011 12:32:16 [0xb09a2000] DEBUG - [com.plexapp.plugins.deredactie] HTTP reply status 200, with 2620 bytes of content.<br />
Feb 13, 2011 12:32:16 [0xb09a2000] DEBUG - [com.plexapp.plugins.deredactie] Completed command GET /video/deredactie in 0.005867 seconds.<br />
Feb 13, 2011 12:32:17 [0xb1140000] DEBUG - TXT record updated: Mac Pro van flubr<br />
Feb 13, 2011 12:32:17 [0xb1244000] DEBUG - Notifying the system bundle an update to server /system/notify/serverUpdated?host=Mac-Pro-van-flubr.local.<br />
Feb 13, 2011 12:32:17 [0xb1244000] DEBUG - [com.plexapp.system] Sending command over HTTP: /system/notify/serverUpdated?host=Mac-Pro-van-flubr.local.<br />
Feb 13, 2011 12:32:17 [0xb0b28000] DEBUG - Request: GET /servers [::ffff:127.0.0.1] (1 live)<br />
Feb 13, 2011 12:32:17 [0xb0b28000] DEBUG - It took 0.000021 sec to serialize a list with 1 elements.<br />
Feb 13, 2011 12:32:17 [0xb0510000] DEBUG - Request: GET /music [::ffff:127.0.0.1] (1 live)<br />
Feb 13, 2011 12:32:17 [0xb0510000] DEBUG - It took 0.000036 sec to serialize a list with 1 elements.<br />
Feb 13, 2011 12:32:17 [0xb0510000] DEBUG - Request: GET /video [::ffff:127.0.0.1] (1 live)<br />
Feb 13, 2011 12:32:17 [0xb0510000] DEBUG - It took 0.000048 sec to serialize a list with 6 elements.<br />
Feb 13, 2011 12:32:17 [0xb0b28000] DEBUG - Request: GET /photos [::ffff:127.0.0.1] (1 live)<br />
Feb 13, 2011 12:32:17 [0xb0b28000] DEBUG - It took 0.000023 sec to serialize a list with 2 elements.<br />
Feb 13, 2011 12:32:17 [0xb09a2000] DEBUG - Request: GET /library/sections [::ffff:127.0.0.1] (1 live)<br />
Feb 13, 2011 12:32:17 [0xb09a2000] DEBUG - It took 0.000820 sec to serialize a list with 3 elements.<br />
Feb 13, 2011 12:32:17 [0xb0510000] DEBUG - Request: GET /library/sections [::ffff:192.168.1.5] (1 live)<br />
Feb 13, 2011 12:32:17 [0xb0510000] DEBUG - It took 0.000741 sec to serialize a list with 3 elements.<br />
Feb 13, 2011 12:32:17 [0xb0510000] DEBUG - Request: GET /video [::ffff:192.168.1.5] (1 live)<br />
Feb 13, 2011 12:32:17 [0xb0510000] DEBUG - It took 0.000053 sec to serialize a list with 6 elements.<br />
Feb 13, 2011 12:32:17 [0xb09a2000] DEBUG - Request: GET /music [::ffff:192.168.1.5] (1 live)<br />
Feb 13, 2011 12:32:17 [0xb09a2000] DEBUG - It took 0.000023 sec to serialize a list with 1 elements.<br />
Feb 13, 2011 12:32:17 [0xb0b28000] DEBUG - Request: GET /photos [::ffff:192.168.1.5] (1 live)<br />
Feb 13, 2011 12:32:17 [0xb0b28000] DEBUG - It took 0.000028 sec to serialize a list with 2 elements.<br />
Feb 13, 2011 12:32:17 [0xb1244000] DEBUG - [com.plexapp.system] HTTP reply status 200, with 0 bytes of content.<br />
Feb 13, 2011 12:32:17 [0xb1244000] DEBUG - [com.plexapp.system] Completed command /system/notify/serverUpdated?host=Mac-Pro-van-flubr.local. in 0.007359 seconds.<br />
Feb 13, 2011 12:32:33 [0xb0510000] DEBUG - Request: GET /video/deredactie/:/function/Videos?function_args=Y2VyZWFsMQozCmRpY3QKZGljdApGcmFtZXdvcmsub2JqZWN0cy5JdGVtSW5mb1JlY29yZAoyCnM1MwpodHRwOi8vd3d3LmRlcmVkYWN0aWUuYmUvY20vdnJ0bmlldXdzL21lZGlhdGhlZWsvd2VlcnM3CnBhZ2VVcmxyMgpzNgpzZW5kZXI1CnM0CldlZXJzOQppdGVtVGl0bGVzMTEKRGUgUmVkYWN0aWVzNgp0aXRsZTFzNApOb25lczYKdGl0bGUyczQ1Ci92aWRlby9kZXJlZGFjdGllLzovcmVzb3VyY2VzL2FydC1kZWZhdWx0LnBuZ3MzCmFydHM0NgovdmlkZW8vZGVyZWRhY3RpZS86L3Jlc291cmNlcy9pY29uLWRlZmF1bHQucG5nczUKdGh1bWJyMQpyMAo_ [::ffff:127.0.0.1] (1 live)<br />
Feb 13, 2011 12:32:33 [0xb0510000] DEBUG -  * function_args => Y2VyZWFsMQozCmRpY3QKZGljdApGcmFtZXdvcmsub2JqZWN0cy5JdGVtSW5mb1JlY29yZAoyCnM1MwpodHRwOi8vd3d3LmRlcmVkYWN0aWUuYmUvY20vdnJ0bmlldXdzL21lZGlhdGhlZWsvd2VlcnM3CnBhZ2VVcmxyMgpzNgpzZW5kZXI1CnM0CldlZXJzOQppdGVtVGl0bGVzMTEKRGUgUmVkYWN0aWVzNgp0aXRsZTFzNApOb25lczYKdGl0bGUyczQ1Ci92aWRlby9kZXJlZGFjdGllLzovcmVzb3VyY2VzL2FydC1kZWZhdWx0LnBuZ3MzCmFydHM0NgovdmlkZW8vZGVyZWRhY3RpZS86L3Jlc291cmNlcy9pY29uLWRlZmF1bHQucG5nczUKdGh1bWJyMQpyMAo_<br />
Feb 13, 2011 12:32:33 [0xb0510000] DEBUG - Plug-in com.plexapp.plugins.deredactie has been used 82 times.<br />
Feb 13, 2011 12:32:33 [0xb0510000] DEBUG - [com.plexapp.plugins.deredactie] Sending command over HTTP: /video/deredactie/:/function/Videos?function_args=Y2VyZWFsMQozCmRpY3QKZGljdApGcmFtZXdvcmsub2JqZWN0cy5JdGVtSW5mb1JlY29yZAoyCnM1MwpodHRwOi8vd3d3LmRlcmVkYWN0aWUuYmUvY20vdnJ0bmlldXdzL21lZGlhdGhlZWsvd2VlcnM3CnBhZ2VVcmxyMgpzNgpzZW5kZXI1CnM0CldlZXJzOQppdGVtVGl0bGVzMTEKRGUgUmVkYWN0aWVzNgp0aXRsZTFzNApOb25lczYKdGl0bGUyczQ1Ci92aWRlby9kZXJlZGFjdGllLzovcmVzb3VyY2VzL2FydC1kZWZhdWx0LnBuZ3MzCmFydHM0NgovdmlkZW8vZGVyZWRhY3RpZS86L3Jlc291cmNlcy9pY29uLWRlZmF1bHQucG5nczUKdGh1bWJyMQpyMAo_<br />
Feb 13, 2011 12:32:33 [0xb0510000] DEBUG - (Capabilities) Adding WebKit.<br />
Feb 13, 2011 12:32:33 [0xb0510000] DEBUG - (Capabilties) Passing down capabilities of 'protocols=http-video,shoutcast,spiff,webkit' to plug-in.<br />
Feb 13, 2011 12:32:37 [0xb0510000] DEBUG - [com.plexapp.plugins.deredactie] HTTP reply status 200, with 229 bytes of content.<br />
[size=3]Feb 13, 2011 12:32:37 [0xb0510000] DEBUG - [com.plexapp.plugins.deredactie] Completed command GET /video/deredactie/:/function/Videos?function_args=Y2VyZWFsMQozCmRpY3QKZGljdApGcmFtZXdvcmsub2JqZWN0cy5JdGVtSW5mb1JlY29yZAoyCnM1MwpodHRwOi8vd3d3LmRlcmVkYWN0aWUuYmUvY20vdnJ0bmlldXdzL21lZGlhdGhlZWsvd2VlcnM3CnBhZ2VVcmxyMgpzNgpzZW5kZXI1CnM0CldlZXJzOQppdGVtVGl0bGVzMTEKRGUgUmVkYWN0aWVzNgp0aXRsZTFzNApOb25lczYKdGl0bGUyczQ1Ci92aWRlby9kZXJlZGFjdGllLzovcmVzb3VyY2VzL2FydC1kZWZhdWx0LnBuZ3MzCmFydHM0NgovdmlkZW8vZGVyZWRhY3RpZS86L3Jlc291cmNlcy9pY29uLWRlZmF1bHQucG5nczUKdGh1bWJyMQpyMAo_ in 0.008307 seconds.

[/size]

If you look at the page source for those html pages you won’t see any of the tags you want in the html. They are being constructed via JavaScript in the browser.



It looks like the original page makes a call to something like this:



http://www.deredactie.be/cm/vrtnieuws/mediatheek/redactietips/redactietips_2eNiveau?view=shortcutDepartment&shortcutView=defaultList



which seems to be an xhtml fragment containing media information. That’s the URL you probably need to access if you can either extract it from the original page or construct it manually if you can figure out the parameter logic.



I couldn’t get much further as the site kept dying on me.



Jonny

Thanks, it worked. I found all the URL’s and now I have a complete working structure of the lists.

Now I only have to fetch the streams.



This is what I found out:


<br />
link = video.xpath("span")[0].get('href')<br />



This command extracts a link of this form:
http://www.deredactie.be/cm/vrtnieuws/mediatheek/nieuws/binnenland/1.963359?view=shortcutArticle&shortcutView=popupPlayer
But the part "?view=shortcutArticle&shortcutView=popupPlayer" should be deleted. Than I've got a link to the page with the stream (so only this: http://www.deredactie.be/cm/vrtnieuws/mediatheek/nieuws/binnenland/1.963359 )
So this is my first question: How can I delete the last part of this URL in python, so that I get a useable URL?

Then my second question:
The URL directs me to a page with a RTMP stream. You can see the source code here:


<br />
<object width="640" height="360" type="application/x-shockwave-flash" id="flash_1847440706" name="flash_1847440706" data="/html/flash/common/player.swf"><br />
	<param name="allowfullscreen" value="true"><br />
	<param name="allowscriptaccess" value="always"><br />
	<param name="bgcolor" value="#000000"><br />
	<param name="wmode" value="transparent"><br />
	<param name="flashvars" value="mediaId=flash_1847440706&amp;image=http://media.vrtnieuws.net/2011/02/162215681ONL1102144131898_urlThumbnailDir_tmb/0045.jpg&amp;autostart=true&amp;provider=rtmp&amp;streamer=rtmp://vrt.flash.streampower.be/vrtnieuws&amp;file=2011/02/162215681ONL1102144131898.urlFLVLong.flv&amp;streamsense_jwp.typestream=vod&amp;streamsense_jwp.playlisttitle=deredactie.mediatheek.nieuws.binnenland.index_id_2-3792&amp;streamsense_jwp.programtitle=veel_schade_na_brandstichting_in_gerechtsgebouw_id_1-963359&amp;streamsense_jwp.episodetitle=Veel schade na brandstichting in gerechtsgebouw&amp;streamsense_jwp.debug=on&amp;bufferlength=5&amp;plugins=/html/flash/common/streamsense_jwp-v1.swf&amp;controlbar=over&amp;skin=/html/flash/common/glow.zip"></object><br />




So I thought that this command would be appropriate:


<br />
dir.Append(RTMPVideoItem(url="rtmp://vrt.flash.streampower.be/vrtnieuws", clip="2011/02/162215681ONL1102144131898.urlFLVLong.flv", width=640, height=360, live=False, title=title, thumb=image))<br />




But this line gives me a black screen and no sound in Plex. However I can see in the control bar that the video is playing and that the duration is correct.
Does this mean that the stream is "locked" and that I should work with a site config and a webVideoItem instead? Or is there another way to solve this? I also tried the RTMP stream with an RTMP stream player at this site, but that also gave me a black window and the error "stream not found, clip....".

try this:

link = video.xpath("span")[0].get('href')<br />
link = link.split('?')[0]





Streams are difficult to get right, but one you do... ahhh!
you might try:

<br />
dir.Append(RTMPVideoItem(url="rtmp://vrt.flash.streampower.be/vrtnieuws", clip="2011/02/162215681ONL1102144131898.urlFLVLong", width=640, height=360, live=False, title=title, thumb=image))<br />



as you are supposed to remove the ".flv" off the clip.
[http://wiki.plexapp....ne_videoplayers](http://wiki.plexapp.com/index.php/Online_videoplayers)

Thanks for the solution! The RTMP stream works fine now (after installing an older version of flash, I only heard sound but no image).



For the link I used this code:


<br />
<br />
for video in content.xpath('//div[@id="defaultList"]/div/div/div'):<br />
        image = video.xpath("img[1]")[0].get('src')<br />
        title = video.xpath("span/a")[0].text<br />
        <br />
        link = video.xpath("span")[0].get('href')<br />
        link = link.split('?')[0]<br />
        content2 = XML.ElementFromURL(link,True)<br />
        summary = content2.xpath('//div[@id="videoMetaData"]/div/p')[0].text<br />
        <br />
        dir.Append(RTMPVideoItem(url="rtmp://vrt.flash.streampower.be/vrtnieuws", clip="2011/02/162215681ONL1102144131898.urlFLVLong", width=640, height=360, live=False, title=title, summary=summary, thumb=image))<br />




This is what I want the code to do:
Split an URL so that a useable URL is left. This URL contains a summary and the RTMP link of a video. Because the summary is easier to extract, I'm starting with this. However something must be wrong, since the list with videos doesn't appear anymore.
I think the best way to debug this, is by checking if the extracted link is correct, and if it is properly being split. So how can I make the link visible in plex for debugging? I tried adding a .text suffix and showing it as summary, but this didn't work.

<br />
Log("Link:"+link)<br />



should do it. (then look in the plugin log)

Thank you all for your help! I just read in this link that Sander1 wrote the plugin two days ago:

http://forums.plexapp.com/index.php/topic/6004-vrt-plug-in/page__gopid__146549#entry146549



His code seems more flexible, so I decided to discontinue my project.



However it was very interesting! I learned a lot and maybe I’ll write another plugin in the future!

Hi! I’m writing a new plugin and it’s almost finished. I just have to extract the RTMP stream, but that’s not going very well.



The source code of the site is in the attachment.



This is the code that should extract the rtmpClip:


<br />
        content2 = HTML.ElementFromURL(link, errors='ignore')<br />
        rtmpClip = content2.xpath('//div[@id="videoZoneContainer"]/div/div/object/param[4]').get('value')<br />
        Log(rtmpClip)<br />
        rtmpClip = rtmpClip.split(".flv&")[0]<br />
        Log(rtmpClip)<br />
        rtmpClip = rtmpClip.split("file=")[1]<br />
        Log(rtmpClip)<br />




The xpath gives me this:


And the log gives me this error:

rtmpClip = content2.xpath('//div[@id="videoZoneContainer"]/div/div/object/param[4]').get('value')
AttributeError: 'list' object has no attribute 'get'

So I was wondering if somebody could help me extracting this: "2011/02/tourneegen_r02_a05_fragm1_net_Website_EEN"

Xpath always return a list. Try adding a [0] before you .get(…

Thanks, but now I get this error:





rtmpClip = inhoud.xpath(’//div[@id=“videoZoneContainer”]/div/div/object/param’)[0].get(‘value’)

File “/Users/flubr/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/2/Python/Framework/bases.py”, line 142, in

getitem = lambda x, y: x.getitem(y),

IndexError: list index out of range



Any suggestions?

I would use



<br />
rtmpClip = content2.xpath('//div[@class="jw-player"]//param[@name="flashvars"]')[0].get('value')<br />




Jonny

That was gonna be my next post … totally agreed with Jonny … :slight_smile: