RE: [SUPPORT] Hulu Video Plugin - jimt35126 - 2014-03-02
Here is the stream_hulu.py code with all changes incorporated so far:
Code: import xbmc
import xbmcgui
import xbmcplugin
import common
import ads
import subtitles
import sys
import binascii
import base64
import os
import hmac
import operator
import time
import urllib
import re
import md5
from array import array
from BeautifulSoup import BeautifulStoneSoup
try:
from xml.etree import ElementTree
except:
from elementtree import ElementTree
smildeckeys = [ common.xmldeckeys[9] ]
class Main:
def __init__( self ):
if 'http://' in common.args.url:
video_id=self.getIDS4HTTP(common.args.url)
self.queue=True
httpplay=True
else:
self.queue=False
httpplay=False
video_id=common.args.url
admodule = ads.Main()
common.playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
if common.args.mode.endswith('TV_play'):
if os.path.isfile(common.ADCACHE):
os.remove(common.ADCACHE)
self.NoResolve=False
self.GUID = common.makeGUID()
if common.args.mode.startswith('Captions'):
common.settings['enable_captions']='true'
common.settings['segmentvideos'] = 'false'
self.NoResolve=True
elif common.args.mode.startswith('NoCaptions'):
common.settings['enable_captions']='false'
common.settings['segmentvideos'] = 'false'
self.NoResolve=True
elif common.args.mode.startswith('Select'):
common.settings['quality']='0'
common.settings['segmentvideos'] = 'false'
self.NoResolve=True
if ((common.settings['segmentvideos'] == 'true') or
(common.settings['networkpreroll'] == 'true') or
(common.settings['prerollads'] > 0) or
(common.settings['trailads'] > 0)):
common.playlist.clear()
# POST VIEW
if common.settings['enable_login']=='true' and common.settings['usertoken']:
common.viewed(common.args.videoid)
if not self.NoResolve:
if (common.settings['networkpreroll'] == 'true'):
self.NetworkPreroll()
addcount = admodule.PreRoll(video_id,self.GUID,self.queue)
if addcount > 0:
self.queue=True
else:
addcount = 0
if common.settings['segmentvideos'] == 'true':
segments = self.playSegment(video_id)
if segments:
adbreaks = common.settings['adbreaks']
for i in range(1,len(segments)+1):
admodule.queueAD(video_id,adbreaks+addcount,addcount)
addcount += adbreaks
self.queueVideoSegment(video_id,segment=i)
else:
self.play(video_id)
admodule.Trailing(addcount,video_id,self.GUID)
if common.settings['queueremove']=='true' and common.settings['enable_login']=='true' and common.settings['usertoken']:
self.queueViewComplete()
if httpplay:
xbmc.Player().play(common.playlist)
elif common.args.mode == 'SEGMENT_play':
self.queue=False
self.NoResolve=False
self.GUID = common.args.guid
self.playSegment(video_id,segment=int(common.args.segment))
elif common.args.mode == 'AD_play':
self.NoResolve=False
self.GUID = common.args.guid
pod = int(common.args.pod)
admodule.playAD(video_id,pod,self.GUID)
elif common.args.mode == 'SUBTITLE_play':
subtitles.Main().SetSubtitles(video_id)
def getIDS4HTTP(self, url):
pagedata=common.getFEED(url)
common.args.videoid = url.split('watch/')[1].split('/')[0]
content_id = re.compile('so.addVariable\("content_id", (.*?)\);').findall(pagedata)[0].strip()
common.args.eid = self.cid2eid(content_id)
return content_id
def cid2eid(self, content_id):
m = md5.new()
m.update(str(content_id) + "MAZxpK3WwazfARjIpSXKQ9cmg9nPe5wIOOfKuBIfz7bNdat6gQKHj69ZWNWNVB1")
value = m.digest()
return base64.encodestring(value).replace("+", "-").replace("/", "_").replace("=", "").replace('\n','')
def getSMIL(self, video_id,retry=0):
epoch = int(time.mktime(time.gmtime()))
parameters = {'video_id' : video_id,
'v' : '888324234',
'ts' : str(epoch),
'np' : '1',
'vp' : '1',
'enable_fa' : '1',
'device_id' : self.GUID,
'pp' : 'Desktop',
'dp_id' : 'Hulu',
'region' : 'US',
'ep' : '1',
'language' : 'en'
}
if retry > 0:
parameters['retry']=str(retry)
if common.settings['enable_login']=='true' and common.settings['enable_plus']=='true' and common.settings['usertoken']:
parameters['token'] = common.settings['usertoken']
smilURL = False
for item1, item2 in parameters.iteritems():
if not smilURL:
smilURL = 'http://s.hulu.com/select?'+item1+'='+item2
else:
smilURL += '&'+item1+'='+item2
smilURL += '&bcs='+self.content_sig(parameters)
print 'HULU --> SMILURL: ' + smilURL
if common.settings['proxy_enable'] == 'true':
proxy=True
else:
proxy=False
smilXML=common.getFEED(smilURL,proxy=proxy)
if smilXML:
smilXML=self.decrypt_SMIL(smilXML)
print "GOT SMIL"
if smilXML:
smilSoup=BeautifulStoneSoup(smilXML, convertEntities=BeautifulStoneSoup.HTML_ENTITIES)
print smilSoup.prettify()
return smilSoup
else:
return False
else:
return False
def content_sig(self, parameters):
hmac_key = 'f6daaa397d51f568dd068709b0ce8e93293e078f7dfc3b40dd8c32d36d2b3ce1'
sorted_parameters = sorted(parameters.iteritems(), key=operator.itemgetter(0))
data = ''
for item1, item2 in sorted_parameters:
data += item1 + item2
sig = hmac.new(hmac_key, data)
return sig.hexdigest()
def decrypt_SMIL(self, encsmil):
encdata = binascii.unhexlify(encsmil)
expire_message = 'Your access to play this content has expired.'
plus_message = 'please close any Hulu Plus videos you may be watching on other devices'
proxy_message = 'you are trying to access Hulu through an anonymous proxy tool'
for key in smildeckeys[:]:
cbc = common.AES_CBC(binascii.unhexlify(key[0]))
smil = cbc.decrypt(encdata,key[1])
print smil
if (smil.find("<smil") == 0):
#print key
i = smil.rfind("</smil>")
smil = smil[0:i+7]
return smil
elif expire_message in smil:
xbmcgui.Dialog().ok('Content Expired',expire_message)
return False
elif plus_message in smil:
xbmcgui.Dialog().ok('Too many sessions','please close any Hulu Plus videos','you may be watching on other devices')
return False
elif proxy_message in smil:
xbmcgui.Dialog().ok('Proxy Detected','Based on your IP address we noticed','you are trying to access Hulu','through an anonymous proxy tool')
return False
def queueViewComplete(self):
u = sys.argv[0]
u += "?mode='viewcomplete'"
u += '&videoid="'+urllib.quote_plus(common.args.videoid)+'"'
item=xbmcgui.ListItem("Remove from Queue")
common.playlist.add(url=u, listitem=item)
def queueVideoSegment( self, video_id, segment=False):
mode='SEGMENT_play'
u = sys.argv[0]
u += '?url="'+urllib.quote_plus(video_id)+'"'
u += '&mode="'+urllib.quote_plus(mode)+'"'
u += '&videoid="'+urllib.quote_plus(common.args.videoid)+'"'
u += '&segment="'+urllib.quote_plus(str(segment))+'"'
u += '&guid="'+urllib.quote_plus(self.GUID)+'"'
item=xbmcgui.ListItem(self.displayname)
item.setInfo( type="Video", infoLabels=self.infoLabels)
item.setProperty('IsPlayable', 'true')
common.playlist.add(url=u, listitem=item)
def time2ms( self, time):
hour,minute,seconds = time.split(';')[0].split(':')
frame = int((float(time.split(';')[1])/24)*1000)
milliseconds = (((int(hour)*60*60)+(int(minute)*60)+int(seconds))*1000)+frame
return milliseconds
def NetworkPreroll( self ):
url = 'http://r.hulu.com/videos?eid='+common.args.eid+'&include=video_assets&include_eos=1&_language=en&_package_group_id=1&_region=US'
data=common.getFEED(url)
tree=BeautifulStoneSoup(data, convertEntities=BeautifulStoneSoup.HTML_ENTITIES)
networkPreroll = tree.find('show').find('link-url').string
if networkPreroll is not None:
if '.flv' in networkPreroll:
name = tree.find('channel').string
infoLabels={ "Title":name }
item = xbmcgui.ListItem(name+' Intro',path=networkPreroll)
item.setInfo( type="Video", infoLabels=infoLabels)
if self.queue:
item.setProperty('IsPlayable', 'true')
common.playlist.add(url=networkPreroll, listitem=item)
else:
self.queue = True
xbmcplugin.setResolvedUrl(common.handle, True, item)
def playSegment( self, video_id, segment=0):
try:
if segments > 0: smilSoup = self.getSMIL(video_id,retry=1)
else: smilSoup = self.getSMIL(video_id)
except: smilSoup = self.getSMIL(video_id,retry=1)
if smilSoup:
finalUrl = self.selectStream(smilSoup)
self.displayname, self.infoLabels, segments = self.getMeta(smilSoup)
segmentUrl = finalUrl
if segments:
segmentUrl = finalUrl
if segment > 0:
startseconds = self.time2ms(segments[segment-1])
segmentUrl += " start="+str(startseconds)
if len(segments) > segment:
stopseconds = self.time2ms(segments[segment])
segmentUrl += " stop="+str(stopseconds)
item = xbmcgui.ListItem(self.displayname,path=segmentUrl)
item.setInfo( type="Video", infoLabels=self.infoLabels)
if self.queue:
item.setProperty('IsPlayable', 'true')
common.playlist.add(url=segmentUrl, listitem=item)
else:
self.queue = True
xbmcplugin.setResolvedUrl(common.handle, True, item)
return segments
def play( self, video_id):
if (common.settings['enable_captions'] == 'true'):
subtitles.Main().checkCaptions(video_id)
try: smilSoup = self.getSMIL(video_id)
except: smilSoup = self.getSMIL(video_id,retry=1)
if smilSoup:
finalUrl = self.selectStream(smilSoup)
displayname, infoLabels, segments = self.getMeta(smilSoup)
#debug testing
finalUrl = urllib.unquote(finalUrl).decode('utf8')
print "finalurl -- > " + finalUrl
#/debug testing
item = xbmcgui.ListItem(displayname,path=finalUrl)
item.setInfo( type="Video", infoLabels=infoLabels)
if self.queue:
item.setProperty('IsPlayable', 'true')
common.playlist.add(url=finalUrl, listitem=item)
else:
self.queue = True
if self.NoResolve:
xbmc.sleep(5)
xbmc.Player().play(finalUrl,item)
else:
xbmcplugin.setResolvedUrl(common.handle, True, item)
if (common.settings['enable_captions'] == 'true'):
subtitles.Main().PlayWaitSubtitles(video_id)
return segments
def getMeta( self, smilSoup ):
refs = smilSoup.findAll('ref')
ref = refs[1]
title = ref['title']
series_title = ref['tp:series_title']
plot = ref['abstract']
try:season = int(ref['tp:season_number'])
except:season = -1
try:episode = int(ref['tp:episode_number'])
except:episode = -1
displayname = series_title+' - '+str(season)+'x'+str(episode)+' - '+title
try:
playlist = refs[0]['src']
mpaa=re.compile('rating,([^\]]+)').findall(playlist)[0]
except: mpaa=''
infoLabels={ "Title":title,
"TVShowTitle":series_title,
"Plot":plot,
"MPAA":mpaa,
"Season":season,
"Episode":episode}
try:
segments = ref['tp:segments']
if segments <> '':
segments=segments.replace('T:','').split(',')
else:
segments = False
except:segments = False
return displayname, infoLabels, segments
def selectStream( self, smilSoup ):
video=smilSoup.findAll('video')
if video is None or len(video) == 0:
xbmcgui.Dialog().ok('No Video Streams','SMIL did not contain video links','Geo-Blocked')
return
streams=[]
selectedStream = None
cdn = None
qtypes=['ask', 'p011', 'p010', 'p009', 'p008', 'H264 Medium', 'H264 650K', 'H264 400K', 'VP6 400K']
qt = int(common.settings['quality'])
if qt < 0 or qt > 8: qt = 0
while qt < 8:
qtext = qtypes[qt]
for vid in video:
streams.append([vid['profile'],vid['cdn'],vid['server'],vid['stream'],vid['token']])
if qtext in vid['profile']:
if vid['cdn'] == common.settings['defaultcdn']:
selectedStream = [vid['server'],vid['stream'],vid['token']]
print selectedStream
cdn = vid['cdn']
break
if qt == 0 or selectedStream != None: break
qt += 1
if qt == 0 or selectedStream == None:
if selectedStream == None:
#ask user for quality level
quality=xbmcgui.Dialog().select('Please select a quality level:', [stream[0]+' ('+stream[1]+')' for stream in streams])
print quality
if quality!=-1:
selectedStream = [streams[quality][2], streams[quality][3], streams[quality][4]]
cdn = streams[quality][1]
print "stream url"
print selectedStream
if selectedStream != None:
server = selectedStream[0]
stream = selectedStream[1]
token = selectedStream[2]
protocolSplit = server.split("://")
pathSplit = protocolSplit[1].split("/")
hostname = pathSplit[0]
appName = protocolSplit[1].split(hostname + "/")[1]
if "level3" in cdn:
appName += "?sessionid=sessionId&" + token
stream = stream[0:len(stream)-4]
finalUrl = server + "?sessionid=sessionId&" + token + " app=" + appName
elif "limelight" in cdn:
appName += '?sessionid=sessionId&' + token
stream = stream[0:len(stream)-4]
finalUrl = server + "?sessionid=sessionId&" + token + " app=" + appName
elif "akamai" in cdn:
appName += '?sessionid=sessionId&' + token
finalUrl = server + "?sessionid=sessionId&" + token + " app=" + appName
elif "edgecast" in cdn:
server=server.replace('.com','.com:80')
appName += '?' + token
finalUrl = server + "?" + token + " app=" + appName
else:
xbmcgui.Dialog().ok('Unsupported Content Delivery Network',cdn+' is unsupported at this time')
return
print "item url -- > " + finalUrl
print "app name -- > " + appName
print "playPath -- > " + stream
#define item
SWFPlayer = 'http://download.hulu.com/huludesktop.swf'
finalUrl += " playpath=" + stream + " swfurl=" + SWFPlayer + " pageurl=" + SWFPlayer + " swfvfy=true"
#if (common.settings['swfverify'] == 'true'):
# finalUrl += " swfvfy=true"
return finalUrl
################# OLD FUNCTIONS
# might be useful
def decrypt_cid(self, p):
cidkey = '48555bbbe9f41981df49895f44c83993a09334d02d17e7a76b237d04c084e342'
v3 = binascii.unhexlify(p)
ecb = common.AES(binascii.unhexlify(cidkey))
return ecb.decrypt(v3).split("~")[0]
def cid2eidOLD(self, p):
import md5
dec_cid = int(p.lstrip('m'), 36)
xor_cid = dec_cid ^ 3735928559 # 0xDEADBEEF
m = md5.new()
m.update(str(xor_cid) + "MAZxpK3WwazfARjIpSXKQ9cmg9nPe5wIOOfKuBIfz7bNdat6gQKHj69ZWNWNVB1")
value = m.digest()
return base64.encodestring(value).replace("+", "-").replace("/", "_").replace("=", "").replace('/n','')
def decrypt_pid(self, p):
import re
cp_strings = [
'6fe8131ca9b01ba011e9b0f5bc08c1c9ebaf65f039e1592d53a30def7fced26c',
'd3802c10649503a60619b709d1278ffff84c1856dfd4097541d55c6740442d8b',
'c402fb2f70c89a0df112c5e38583f9202a96c6de3fa1aa3da6849bb317a983b3',
'e1a28374f5562768c061f22394a556a75860f132432415d67768e0c112c31495',
'd3802c10649503a60619b709d1278efef84c1856dfd4097541d55c6740442d8b'
]
v3 = p.split("~")
v3a = binascii.unhexlify(v3[0])
v3b = binascii.unhexlify(v3[1])
ecb = common.AES(v3b)
tmp = ecb.decrypt(v3a)
for v1 in cp_strings[:]:
ecb = common.AES(binascii.unhexlify(v1))
v2 = ecb.decrypt(tmp)
if (re.match("[0-9A-Za-z_-]{32}", v2)):
return v2
def pid_auth(self, pid):
import md5
m=md5.new()
m.update(str(pid) + "yumUsWUfrAPraRaNe2ru2exAXEfaP6Nugubepreb68REt7daS79fase9haqar9sa")
return m.hexdigest()
Sorry, guys, I didn't know where else to post the above code, and I was concerned it was getting lost in the thread. It is easy now to drag your mouse down to the bottom to highlight the above code, press ctrl-c, and then open up stream_hulu.py, press ctrl-a to highlight all the lines, then press ctrl-v to copy in the replacement code.
Thanks ~Jim
RE: [SUPPORT] Hulu Video Plugin - learningit - 2014-03-02
Deleted
RE: [SUPPORT] Hulu Video Plugin - Eye in the sky - 2014-03-02
Works for me in Germany with Default CDN set to akamai and Fallback CDN set to darwin-edgecast.
Thanks.
RE: [SUPPORT] Hulu Video Plugin - vojtishek - 2014-03-02
Hi all, could you please advise with following error message in hulu plugin
http://pastebin.com/hwm8MafL
I'm using Frodo 12.2 on openelec. My location is non US.
Thank you
RE: [SUPPORT] Hulu Video Plugin - ShaDoWoLf - 2014-03-02
I've just installed the add-on provided by learningit and it works for me. I'm in Australia using a DNS redirector. It seems that the fall back CDN has to be set to darwin-edgecast to work. When I left it to NONE it threw an error.
RE: [SUPPORT] Hulu Video Plugin - moneymaker - 2014-03-02
I'd like to inform all coders to pull fixes in github, so I can merge them. Post a request here if you like to have a master status on github.
RE: [SUPPORT] Hulu Video Plugin - ShaDoWoLf - 2014-03-02
Anyone know how to or where the settings for the watched/unwatched status of videos for your subscriptions are kept in the OS filesystem? I'd like to merge them with the new add-on from the old one.
RE: [SUPPORT] Hulu Video Plugin - frieten - 2014-03-02
wrong app. sorry delete
RE: [SUPPORT] Hulu Video Plugin - learningit - 2014-03-02
(2014-03-02, 13:19)moneymaker Wrote: I'd like to inform all coders to pull fixes in github, so I can merge them. Post a request here if you like to have a master status on github.
The reason that I didn't do that - I'd love to for support reasons, is that that my repo isn't a fork of yours or for that matter, Bluecop's. I need some guidance on how to do a pull request against a non-forked repo in Github. As I said, my code just plain evolved in place from the original Bluecop addon, I never forked it from his repo. I put my repo up so that you could see what changes I've made as it was getting tedious and error prone having individual files being exchanged among so many users. As I said, I'll probably regret putting the repo up. I've taken down my previous posting so as not to confuse matters.
.
RE: [SUPPORT] Hulu Video Plugin - moneymaker - 2014-03-02
@learningit
- Creating a pull request
- Using Pull Requests
- Pull Request Tutorial
You can collaborate, too.
RE: [SUPPORT] Hulu Video Plugin - learningit - 2014-03-02
(2014-03-02, 18:34)moneymaker Wrote: @learningit
- Creating a pull request
- Using Pull Requests
- Pull Request Tutorial
You can collaborate, too. I am very well aware how github works.
Yes, I tried a fork&pull yesterday and ended up with an unworkable mess. I suggest that if you want to do this we take the conversation separately.
Without more test by users on my version I don't know if this is worth the time or trouble, but there are some really fundamental changes that are needed in the beta version. I would suggest that meeting the minimum standards for acceptance into the XBMC.org repo be met - such as not writing into the addon folder.
Acceptance in the official repo is a goal that would benefit all XBMC users.
RE: [SUPPORT] Hulu Video Plugin - KonaHilo - 2014-03-02
Thank you, learningit. This works for me in the US. Glad to have someone concerning themselves with the current App.
I suggest the moderators consider 'forking' (is that the right word?) this sub-forum in two, with one set of threads specifically for the current version and the other for the beta version. As long as this app is being developed, there will probably be the two versions of it, and less confusion will result from having separate sub-forums for them.
Thanks again!
(2014-03-02, 06:28)learningit Wrote: Deleted
RE: [SUPPORT] Hulu Video Plugin - moneymaker - 2014-03-02
@learningit, KonaHilo
I'll see what I can do.
To make it understandable:
# BlueCop's repository - Old version
# xbmcplus repository - Hulu and Hulu (beta) [I've taken the BlueCop's add-on to my repository so that as many as possible can help] {BlueCop collaborate}
xbmcplus repository:
# Hulu (there are some fixes)
# Hulu (beta) - will be merged with Hulu if it's someday works again
RE: [SUPPORT] Hulu Video Plugin - learningit - 2014-03-03
Because so many have asked me to put the link back up, I will be posting it in a new forum under hulu new. After looking at the xbmcplus copy, I realize that we are going after different problems - I want to get this back in shape based on the XBMC.org repo development rules and don't care about realdebrid issues which are really the only changes, other than cosmetic ones made in the xbmcplus repo. As I said before, I'll probably regret it.
RE: [SUPPORT] Hulu Video Plugin - TamaraKama - 2014-03-03
Edgecast seems to have disappeared as a choice for me now... how about the rest of you? Also, real-debrid is not working. Could they have been using edgecast as well?
Logs...
Hulu Login: https://dl.dropboxusercontent.com/u/28944/xbmc-hululogin.log
Real-Debrid: https://dl.dropboxusercontent.com/u/28944/xbmc-realdebrid.log
|