Hulu Plugin Development Thread - Developers only!

  Thread Rating:
  • 0 Votes - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Post Reply
rectalogic Offline
Junior Member
Posts: 30
Joined: Feb 2009
Reputation: 0
Post: #16
rwparris2 Wrote:I'll test that string when I get home.

Those options work on Mac/Linux too I believe. I just used the long options because they are more readable and I didn't know they weren't supported on Windows.


Here is a patch to use the short options:

Code:
diff --git a/resources/lib/decswf.py b/resources/lib/decswf.py
index eec6eff..cbcb56a 100644
--- a/resources/lib/decswf.py
+++ b/resources/lib/decswf.py
@@ -10,7 +10,7 @@ PID_RE = re.compile("hulupid=(.*)")
def hulu_decrypt(pid):
     gnash = common.settings['gnash_path']
     swf = os.path.join(os.path.dirname(os.path.realpath(__file__)), "DecryptPid.swf")
-    args = [gnash, "--render-mode", "0", "--once", "--verbose", "--param", "FlashVars=pid=%s" % pid, swf]
+    args = [gnash, "-r", "0", "-1", "-v", "-P", "FlashVars=pid=%s" % pid, swf]
     output = subprocess.Popen(args, executable=gnash, bufsize=1, universal_newlines=True, stdout=subprocess.PIPE).communicate()[0]
     match = PID_RE.search(output)
     if match:
find quote
ptaylor Offline
Junior Member
Posts: 16
Joined: Feb 2009
Reputation: 0
Post: #17
mgandalf Wrote:Regarding missing season episodes due to how the plugin grabs the rss feed which seems to only have 20 entries, I've created a short script to parse out all the episodes based on a given season. I'm not at all familiar with Python, so I wrote this as a proof of concept in Perl.

Maybe someone can adapt this to Python?

Mark,

A few weeks ago I ran into this same issue and learned enough python to write the below code (with bits of the original python function in-tact). I posted it in the other thread, but I don't think it has been implemented in the plug-in yet (or perhaps rwparris2 has another method in mind to take care of this issue).

PHP Code:
def addEpisodeListself ):
        
#initialize variables
        
p=re.compile('(\d+)')#gets last number from "season ##"
        
currentSeason=p.findall(common.args.name)[0]
        
        
html common.getHTML(common.args.url)
        
# Get Ajax URL for all seasons
        
ajaxMatches re.finditer('http://www.hulu.com/videos/season_expander',html)
        
        for 
match in ajaxMatches:
            
endPos html.find(""",match.start())
            
nextUrl html[match.start():endPos].replace("&","&")
            
            
# Determine Season
            
startPos nextUrl.find('season_number=')
            
seasonNum nextUrl[(startPos+14):(startPos+16)].replace('&','')
            
            if 
seasonNum == currentSeason:
                
#Read the Ajax for this season
                
ajax common.getHTML(nextUrl)
                
ajax ajax.replace('\\"','"').replace('\\076','>').replace('\\074','<')
            
                
soup BeautifulSoup(ajax)
                
episodeData soup.findAll("td", { "class" re.compile('^c[0-13-6]') })    
                
                for 
episode in episodeData:
                    if 
str(episode).find('class="c0"') > -1:   # Episode Number
                        
episodeNum episode.renderContents()
                    
elif str(episode).find('class="c1"') > -1# Link and Title
                        
episodeLink episode.renderContents()
                        
tmp BeautifulSoup(episodeLink).find('a')
                        
url tmp['href'].split('#')[0]
                        
episodeName tmp.renderContents()
                    
elif str(episode).find('class="c3"') > -1# Duration
                        
episodeDuration episode.renderContents()
                        
duration=episodeDuration.split(':')
                        
duration=(int(duration[0])*60)+int(duration[1])
                    
elif str(episode).find('class="c4"') > -1# Air Date
                        
airdate episode.renderContents()
                    
elif str(episode).find('class="c6"') > -1# Queue and icon link?
                        
if len(seasonNum)<2:seasonNum='0'+seasonNum
                        
if len(episodeNum)<2:episodeNum='0'+episodeNum
                        name 
's'+seasonNum+'e'+episodeNum+' '+episodeName
                        thumb 
''
                        
plot ''
                        
common.addDirectory(nameurl,'TV_play'thumbthumbcommon.args.fanartplot'genre'

If you put that into the _tv.py file, replacing the existing addEpisodeList, set the "flat_season" variable in your settings.xml to default of 1, and delete the _ty.pyo file (which will automatically be re-gen'ed next time you run the Hulu plug-in), then this will do what you are looking for.

Oh - Caveats of this code:
1. No thumbnails of the episodes themselves
2. No plot descriptions
3. It was written by someone completely new to Python, so I'm sure there are more pythony ways to do things.
(This post was last modified: 2009-02-24 04:17 by ptaylor.)
find quote
TheB Offline
Junior Member
Posts: 3
Joined: Feb 2009
Reputation: 0
Post: #18
I think there may be issues in the current SVN win32 builds with popen (see the bottom of this thread)

To get around this I edited my decswf.py to look like this:
PHP Code:
#!/usr/bin/python

import os
import re
import subprocess
import common
import string

PID_RE 
re.compile("hulupid=(.*)")

def hulu_decrypt(pid):
    
gnash common.settings['gnash_path']
    
swf os.path.join(os.path.dirname(os.path.realpath(__file__)), "DecryptPid.swf")
    
#args = [gnash, "--render-mode", "0", "--once", "--verbose", "--param", "FlashVars=pid=%s" % pid, swf]
    #output = subprocess.Popen(args, executable=None, bufsize=1, universal_newlines=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
    
    
txtout os.path.join(os.path.dirname(os.path.realpath(__file__)), 'tmp.txt')
    
os.system(gnash+'gnash.exe -r 0 -1 -v -P '+"FlashVars=pid=%s" pid+' "'+swf+'" > "'+txtout+'"')
    
output string.join(open(txtout'r').readlines(), "\n")
    
    
match PID_RE.search(output)
    if 
match:
        return 
match.group(1)
    else:
        return 
None 

Now I am getting the decoded pid but the script is still failing with
Code:
start of HULU plugin
19:12:52 T:2548 M:271749120  NOTICE:
19:12:52 T:2548 M:271749120  NOTICE: HULU--> common.args.mode -- > RSS_play
19:12:52 T:2548 M:271749120  NOTICE:
19:12:52 T:2548 M:270704640  NOTICE: http://www.hulu.com/watch/58680/the-fabulous-baker-boys-calling-him-out#http%3A%2F%2Fwww.hulu.com%2Ffeed%2Frecent%2Fmovies.rss%3Frd%3D0
19:12:52 T:2548 M:270704640  NOTICE:
19:12:52 T:2548 M:270704640  NOTICE: HULU --> common :: getHTML :: url = http://www.hulu.com/watch/58680/the-fabulous-baker-boys-calling-him-out#http%3A%2F%2Fwww.hulu.com%2Ffeed%2Frecent%2Fmovies.rss%3Frd%3D0
19:12:52 T:2548 M:270704640  NOTICE:
19:12:52 T:2548 M:271323136  NOTICE: HULU --> common :: getHTML :: url = http://r.hulu.com/videos?content_id=14263288
19:12:52 T:2548 M:271323136  NOTICE:
19:12:55 T:2548 M:270995456  NOTICE: HULU --> SMILURL: http://releasegeo.hulu.com/content.select?pid=c8aa0b0a43c3fc1b979eee8b1cbf594cb5c427c26aa83c7773b737b8e5f98​2b2~6d579943a5d66e396bb5aa24ed17f22524ddd479c804355983585f42d49bdfb8
&mbr=true&format=smil
19:12:55 T:2548 M:270995456  NOTICE:
19:12:55 T:2548 M:270995456  NOTICE: HULU --> common :: getHTML :: url = http://releasegeo.hulu.com/content.select?pid=c8aa0b0a43c3fc1b979eee8b1cbf594cb5c427c26aa83c7773b737b8e5f98​2b2~6d579943a5d66e396bb5aa24ed17f22524ddd479c804355983585f42d49bdfb8
&mbr=true&format=smil
19:12:55 T:2548 M:270995456  NOTICE:
19:12:56 T:2548 M:271454208  NOTICE: Traceback (most recent call last):
19:12:56 T:2548 M:271454208  NOTICE:   File "C:\Documents and Settings\Ryan\Application Data\XBMC\plugins\video\Hulu\default.py", line 60, in ?
19:12:56 T:2548 M:271454208  NOTICE:
19:12:56 T:2548 M:271454208  NOTICE: modes ( )
19:12:56 T:2548 M:271450112  NOTICE:   File "C:\Documents and Settings\Ryan\Application Data\XBMC\plugins\video\Hulu\default.py", line 36, in modes
19:12:56 T:2548 M:271450112  NOTICE:
19:12:56 T:2548 M:271450112  NOTICE: stream_media.Main()
19:12:56 T:2548 M:271446016  NOTICE:   File "C:\Documents and Settings\Ryan\Application Data\XBMC\plugins\video\Hulu\resources\lib\stream_hulu.py", line 14, in __init__
19:12:56 T:2548 M:271446016  NOTICE:
19:12:56 T:2548 M:271446016  NOTICE: self.play()
19:12:56 T:2548 M:271441920  NOTICE:   File "C:\Documents and Settings\Ryan\Application Data\XBMC\plugins\video\Hulu\resources\lib\stream_hulu.py", line 33, in play
19:12:56 T:2548 M:271441920  NOTICE:
19:12:56 T:2548 M:271441920  NOTICE: smilXML=common.getHTML(smilURL)
19:12:56 T:2548 M:271437824  NOTICE:   File "C:\Documents and Settings\Ryan\Application Data\XBMC\plugins\video\Hulu\resources\lib\common.py", line 168, in getHTML
19:12:56 T:2548 M:271433728  NOTICE:
19:12:56 T:2548 M:271433728  NOTICE: response=usock.read()
19:12:56 T:2548 M:271409152  NOTICE:   File "special:\\xbmc\system\python\python24.zlib\socket.py", line 285, in read
19:12:56 T:2548 M:271384576  NOTICE:   File "special:\\xbmc\system\python\python24.zlib\httplib.py", line 478, in read
19:12:56 T:2548 M:271384576  NOTICE: TypeError
19:12:56 T:2548 M:271384576  NOTICE: :
19:12:56 T:2548 M:271380480  NOTICE: unsupported operand type(s) for -=: 'str' and 'int'
19:12:56 T:2548 M:271380480  NOTICE:
19:12:56 T:2548 M:271380480   ERROR: Scriptresult: Error
19:12:58 T:1608 M:273436672   ERROR: DIRECTORY::CDirectory::GetDirectory - Error getting plugin://video/Hulu/?url="http%3A%2F%2Fwww.hulu.com%2Fwatch%2F58680%2Fthe-fabulous-baker-boys-calling-him-out%23http%253A%252F%252Fwww.hulu.com%252Ffeed%252Frecent%252Fmovies.rss%253Frd%​253D0"&mode="RSS_play"&name="The+Fabulous+Baker+Boys%3A+Calling+Him+Out"&fanart="http%3A%2F%2Fassets.hulu.com%2Fshows%2Fkey_art_the_fabulous_baker_boys.jpg"&plot="Susie+points+out+all+of+Jacks+flaws."&genre="HD Gallery"
19:12:58 T:1608 M:273436672   ERROR: CGUIMediaWindow::GetDirectory(plugin://video/Hulu/?url="http%3A%2F%2Fwww.hulu.com%2Fwatch%2F58680%2Fthe-fabulous-baker-boys-calling-him-out%23http%253A%252F%252Fwww.hulu.com%252Ffeed%252Frecent%252Fmovies.rss%253Frd%​253D0"&mode="RSS_play"&name="The+Fabulous+Baker+Boys%3A+Calling+Him+Out"&fanart="http%3A%2F%2Fassets.hulu.com%2Fshows%2Fkey_art_the_fabulous_baker_boys.jpg"&plot="Susie+points+out+all+of+Jacks+flaws."&genre="HD Gallery") failed

Anyone know whats wrong?

BTW: I'm currently using ikons Rev18025 SVN build
find quote
jonm42 Offline
Senior Member
Posts: 177
Joined: Feb 2009
Reputation: 0
Location: Portland, OR
Post: #19
I'll take a stab at this tomorrow -- I was just mucking in this area today. (I'm referring to the episode scan, not the popen on windows; didn't refresh before posting Sad).
(This post was last modified: 2009-02-24 18:33 by jonm42.)
find quote
rwparris2 Offline
Team-XBMC Python Developer
Posts: 1,333
Joined: Jan 2008
Reputation: 2
Location: US
Post: #20
TheB Wrote:I think there may be issues in the current SVN win32 builds with popen (see the bottom of this thread)

yep, popen is broken on win32 for xbmc. Sad

I can't get gnash to work on my system, and after a long day at work I don't want to play with it, but this is what I have so far, and I believe it would work if gnash was...

Code:
#!/usr/bin/python

import os
import re
import subprocess
import common

PID_RE = re.compile("hulupid=(.*)")

def hulu_decrypt(pid):
    gnash = common.settings['gnash_path']
    currentPath = os.path.dirname(os.path.realpath(__file__))
    swf = os.path.join(currentPath, "DecryptPid.swf")
    if os.environ.get( 'OS', 'xbox' ) == 'win32':
        sysargs = '"%s" -r 0 -1 -v -P FlashVars=pid=%s' % (swf, pid)
        outputFilePath = os.path.join(currentPath,'output.txt')
        os.system('"%sgnash" %s>"%s"' %(gnash,sysargs,outputFilePath))
        outputFile = open(outputFilePath,'r')
        output = outputFile.read()
        outputFile.close()
        os.remove(outputFilePath)
    else:
        args = [gnash, "--render-mode", "0", "--once", "--verbose", "--param", "FlashVars=pid=%s" % pid, swf]
        output = subprocess.Popen(args, executable=gnash, bufsize=1, universal_newlines=True, stdout=subprocess.PIPE).communicate()[0]
    print output
    match = PID_RE.search(output)
    if match:
        return match.group(1)

Always read the XBMC online-manual, FAQ and search and search the forum before posting.
For troubleshooting and bug reporting please read how to submit a proper bug report.

If you're interested in writing addons for xbmc, read docs and how-to for plugins and scripts ||| http://code.google.com/p/xbmc-addons/
find quote
jonm42 Offline
Senior Member
Posts: 177
Joined: Feb 2009
Reputation: 0
Location: Portland, OR
Post: #21
Added an issue re: fixing some stuff in subscriptions (will see all of them now), some general cleanup in various spots, and adding clip support within subscriptions.

See http://code.google.com/p/xbmc-addons/iss...tail?id=11; this includes a diff from r656 and a zip with the whole thing.
find quote
hrcolb0 Offline
Member
Posts: 61
Joined: Jan 2009
Reputation: 0
Post: #22
*edited out* I made a boner mistake
(This post was last modified: 2009-02-25 04:03 by hrcolb0.)
find quote
jonm42 Offline
Senior Member
Posts: 177
Joined: Feb 2009
Reputation: 0
Location: Portland, OR
Post: #23
Grumble. See issues # 14: http://code.google.com/p/xbmc-addons/iss...tail?id=14
find quote
jonm42 Offline
Senior Member
Posts: 177
Joined: Feb 2009
Reputation: 0
Location: Portland, OR
Post: #24
I was digging through the page source for the Babylon-5 top level page, and found the following snippet that builds the thumbnail set at the top (the 5 thumbs you see):

Code:
<script type="text/javascript">
//<![CDATA[
var s14qdsk1 = new VideoSlider("s14qdsk1", {url: "http://www.hulu.com/videos/slider", maxCount: 45, urlOptions: {type: "episode", show_id: 806, sort: "original_premiere_date", items_per_page: 5, season: "", page: 1}, seasonCounts: {all: {all: 45, s1: 23, s2: 22}, episode: {all: 45, s1: 23, s2: 22}, clip: {all: 0, s1: 0, s2: 0}}})
//]]>
</script>

which appears to be how they build the set; I also noticed that it had an episode count by season, and a clip count by season. I also noticed that it appears to be a URL call to go get things. Does anyone have an idea as to how to pass in the seasonCounts parameter via a URL? The rest seems pretty straightforward.

Thanks.
find quote
jonm42 Offline
Senior Member
Posts: 177
Joined: Feb 2009
Reputation: 0
Location: Portland, OR
Post: #25
YeeHaw! Try the following in your browser of choice:

http://www.hulu.com/videos/slider?type=e...er_page=45

I'm going to integrate this into a variant of the Hulu plugin and see how far I can get, but I think we have seasons, episodes, names, links, and thumbs.

(Note that adding &season=# gives you season specifics -- Yay!)
(This post was last modified: 2009-02-27 22:30 by jonm42.)
find quote
rwparris2 Offline
Team-XBMC Python Developer
Posts: 1,333
Joined: Jan 2008
Reputation: 2
Location: US
Post: #26
jonm42 Wrote:YeeHaw! Try the following in your browser of choice:

http://www.hulu.com/videos/slider?type=e...er_page=45

I'm going to integrate this into a variant of the Hulu plugin and see how far I can get, but I think we have seasons, episodes, names, links, and thumbs.

(Note that adding &season=# gives you season specifics -- Yay!)

which is exactly how it used to work Wink (EDIT: er, not really)

I went back to look and I was wrong, it uses the 'expander' url for seasons & episodes instead of the slider one you posted above:: http://www.hulu.com/videos/expander?orde...pe=episode

I believe this messy pile of crap does it: http://code.google.com/p/rwparris2-xbmc-...lu.py?r=64

Using the slider will be better, for this though.

But I was using a similar 'slider' url for grabbing the HD streams (which as it turns out xbmc can't play anyways) http://www.hulu.com/videos/slider?season...how_id=164

Anyways, to answer your question from the PM, I moved away from this because the RSS feed has the icon, plot & other info all in one spot in a single easily parsable page. I just didn't test with anything that had more than 20 episodes, so I never noticed that little bug. The fact that you have to make a seperate json request for EVERY episode to get it's plot is really a time killer... and that is why that option exists in the plugin settings, even though it currently does nothing.


You may have figured all this out already, but just a few friendly FYIs:

to get the show info you'll need to grab this number [html]http://www.hulu.com/watch/21074/babylon-5-the-fall-of-night[/html]and then stick it in here:
[html]http://www.hulu.com/videos/info/21074[/html]which will return all kinds of nifty metadata

for shows with categories you'll need to add category= in your url as well, ex. http://www.hulu.com/videos/slider?type=e...t&season=1

for clips just change type=clip (but only if the show actually has clips)

Always read the XBMC online-manual, FAQ and search and search the forum before posting.
For troubleshooting and bug reporting please read how to submit a proper bug report.

If you're interested in writing addons for xbmc, read docs and how-to for plugins and scripts ||| http://code.google.com/p/xbmc-addons/
(This post was last modified: 2009-02-28 00:35 by rwparris2.)
find quote
jonm42 Offline
Senior Member
Posts: 177
Joined: Feb 2009
Reputation: 0
Location: Portland, OR
Post: #27
With the above I can get something together over the weekend; I'll skip plot for now and just structure it so I (or some other lucky person) can fold it in based on the existing option. Thanks! This is a hoot.
(This post was last modified: 2009-02-28 00:29 by jonm42.)
find quote
mileszim Offline
Junior Member
Posts: 2
Joined: Feb 2009
Reputation: 0
Post: #28
And idea for the decryption, what if we had a site that took the PID, and returned the decrypted PID. It could look something like this:

1. hulu plugin retrieves the PID
2. PID -> external site API, so like http://somesite.com/pid=PID
3. Site returns decrypted PID
4. hulu plugin then uses that

It would avoid gnash on XBMC itself, re-enabling the plugin for use with the Xbox, and correct me if I'm wrong but it would most likely use very little resources on the site itself. If that is the case, I could help develop the web-backend part and host it on my site.
find quote
angrycamel Offline
Senior Member
Posts: 233
Joined: Dec 2008
Reputation: 8
Post: #29
mileszim Wrote:And idea for the decryption, what if we had a site that took the PID, and returned the decrypted PID. It could look something like this:

1. hulu plugin retrieves the PID
2. PID -> external site API, so like http://somesite.com/pid=PID
3. Site returns decrypted PID
4. hulu plugin then uses that

It would avoid gnash on XBMC itself, re-enabling the plugin for use with the Xbox, and correct me if I'm wrong but it would most likely use very little resources on the site itself. If that is the case, I could help develop the web-backend part and host it on my site.

I posted that very solution a while back. It's still sitting out on my site if you want o play with it.

http://forum.xbmc.org/showpost.php?p=286...tcount=576

Integrating with XBMC through the JSON-RPC interface?

Try the JSON-RPC Browser

[Image: hUryuD3.png]
find quote
mileszim Offline
Junior Member
Posts: 2
Joined: Feb 2009
Reputation: 0
Post: #30
angrycamel Wrote:I posted that very solution a while back. It's still sitting out on my site if you want o play with it.

http://forum.xbmc.org/showpost.php?p=286...tcount=576

brilliant! i'll try to see if i can implement this. great work as usual angrycamel!
find quote
Post Reply