Guest User

Untitled

a guest
Aug 12th, 2012
192
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.74 KB | None | 0 0
  1. '''
  2.   YouTube plugin for XBMC
  3.    Copyright (C) 2010-2011 Tobias Ussing And Henrik Mosgaard Jensen
  4.  
  5.    This program is free software: you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation, either version 3 of the License, or
  8.    (at your option) any later version.
  9.    
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. '''
  18.  
  19. import sys, urllib, re, os.path, datetime, time, string
  20. import simplejson as json
  21. import xbmc, xbmcgui, xbmcplugin
  22. import YouTubeCore
  23. from xml.dom.minidom import parseString
  24.  
  25. core = YouTubeCore.YouTubeCore()
  26.  
  27. class YouTubePlayer:
  28.     __settings__ = sys.modules[ "__main__" ].__settings__
  29.     __language__ = sys.modules[ "__main__" ].__language__
  30.     __plugin__ = sys.modules[ "__main__" ].__plugin__
  31.     __dbg__ = sys.modules[ "__main__" ].__dbg__
  32.     VALID_CHARS = "-_.() %s%s" % (string.ascii_letters, string.digits)
  33.     USERAGENT = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8"
  34.     urls = {}
  35.    
  36.     def __init__(self):
  37.         # YouTube Playback Feeds
  38.         self.urls['video_stream'] = "http://www.youtube.com/watch?v=%s&safeSearch=none"
  39.         self.urls['embed_stream'] = "http://www.youtube.com/get_video_info?video_id=%s"
  40.         self.urls['timed_text_index'] = "http://www.youtube.com/api/timedtext?type=list&v=%s"
  41.         self.urls['video_info'] = "http://gdata.youtube.com/feeds/api/videos/%s"
  42.         self.urls['close_caption_url'] = "http://www.youtube.com/api/timedtext?type=track&v=%s&name=%s&lang=%s"
  43.         self.urls['transcription_url'] = "http://www.youtube.com/api/timedtext?sparams=asr_langs,caps,expire,v&asr_langs=en,ja&caps=asr&expire=%s&key=yttt1&signature=%s&hl=en&type=trackformat=1&lang=en&kind=asr&name=&v=%s&tlang=en"
  44.         self.urls['annotation_url'] = "http://www.youtube.com/api/reviews/y/read2?video_id=%s"
  45.         self.urls['remove_watch_later'] = "http://www.youtube.com/addto_ajax?action_delete_from_playlist=1"
  46.    
  47.     # ================================ Video Playback ====================================
  48.    
  49.     def playVideo(self, params = {}):
  50.         get = params.get
  51.        
  52.         (video, status) = self.getVideoObject(params);
  53.        
  54.         if status != 200:
  55.             if self.__dbg__ :
  56.                 print self.__plugin__ + " construct video url failed contents of video item " + repr(video)
  57.             self.showErrorMessage(self.__language__(30603), video["apierror"], status)
  58.             return False
  59.        
  60.         listitem=xbmcgui.ListItem(label=video['Title'], iconImage=video['thumbnail'], thumbnailImage=video['thumbnail'], path=video['video_url']);     
  61.         listitem.setInfo(type='Video', infoLabels=video)
  62.        
  63.         if self.__dbg__:
  64.             print self.__plugin__ + " - Playing video: " + self.makeAscii(video['Title']) + " - " + get('videoid') + " - " + video['video_url']
  65.        
  66.         xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), succeeded=True, listitem=listitem)
  67.                    
  68.         self.__settings__.setSetting( "vidstatus-" + video['videoid'], "7" )
  69.  
  70.     def getVideoUrlMap(self, pl_obj, video = {}):
  71.         if self.__dbg__:
  72.             print self.__plugin__ + " getVideoUrlMap: "
  73.         links = {}
  74.         video["url_map"] = "true"
  75.        
  76.         html = ""
  77.         if pl_obj["args"].has_key("fmt_stream_map"):
  78.             html = pl_obj["args"]["fmt_stream_map"]
  79.        
  80.         if len(html) == 0 and pl_obj["args"].has_key("url_encoded_fmt_stream_map"):
  81.             html = urllib.unquote(pl_obj["args"]["url_encoded_fmt_stream_map"])
  82.        
  83.         if len(html) == 0 and pl_obj["args"].has_key("fmt_url_map"):
  84.             html = pl_obj["args"]["fmt_url_map"]   
  85.        
  86.         html = urllib.unquote_plus(html)
  87.        
  88.         if pl_obj["args"].has_key("liveplayback_module"):
  89.             video["live_play"] = "true"
  90.        
  91.         fmt_url_map = [html]
  92.         if html.find("|") > -1:
  93.             fmt_url_map = html.split('|')
  94.         elif html.find(",url=") > -1:
  95.             fmt_url_map = html.split(',url=')
  96.         elif html.find("&conn=") > -1:
  97.             video["stream_map"] = "true"
  98.             fmt_url_map = html.split('&conn=')
  99.        
  100.         print self.__plugin__ + " getVideoUrlMap Searching for fmt_url_map 2: "  + repr(fmt_url_map)
  101.        
  102.         if len(fmt_url_map) > 0:
  103.             for index, fmt_url in enumerate(fmt_url_map):
  104.                 if fmt_url.find("&url") > -1:
  105.                     fmt_url = fmt_url.split("&url")
  106.                     fmt_url_map += [fmt_url[1]]
  107.                     fmt_url = fmt_url[0]
  108.  
  109.                 if (len(fmt_url) > 7 and fmt_url.find("&") > 7):
  110.                     quality = "5"
  111.                     final_url = fmt_url.replace(" ", "%20").replace("url=", "")
  112.                    
  113.                     if (final_url.rfind(';') > 0):
  114.                         final_url = final_url[:final_url.rfind(';')]
  115.                    
  116.                     if (final_url.rfind(',') > final_url.rfind('&id=')):
  117.                         final_url = final_url[:final_url.rfind(',')]
  118.                     elif (final_url.rfind(',') > final_url.rfind('/id/') and final_url.rfind('/id/') > 0):
  119.                         final_url = final_url[:final_url.rfind('/')]
  120.  
  121.                     if (final_url.rfind('itag=') > 0):
  122.                         quality = final_url[final_url.rfind('itag=') + 5:]
  123.                         if quality.find('&') > -1:
  124.                             quality = quality[:quality.find('&')]
  125.                         if quality.find(',') > -1:
  126.                             quality = quality[:quality.find(',')]
  127.                     elif (final_url.rfind('/itag/') > 0):
  128.                         quality = final_url[final_url.rfind('/itag/') + 6:]
  129.                    
  130.                     if final_url.find("&type") > 0:
  131.                         final_url = final_url[:final_url.find("&type")]
  132.                     if self.__settings__.getSetting("preferred") == "true":
  133.                         pos = final_url.find("://")
  134.                         fpos = final_url.find("fallback_host")
  135.                         if pos > -1 and fpos > -1:
  136.                             host = final_url[pos + 3:]
  137.                             if host.find("/") > -1:
  138.                                 host = host[:host.find("/")]
  139.                             fmt_fallback = final_url[fpos + 14:]
  140.                             if fmt_fallback.find("&") > -1:
  141.                                 fmt_fallback = fmt_fallback[:fmt_fallback.find("&")]
  142.                             final_url = final_url.replace(host, fmt_fallback)
  143.                             final_url = final_url.replace("fallback_host=" + fmt_fallback, "fallback_host=" + host)
  144.  
  145.                     if final_url.find("rtmp") > -1 and index > 0:
  146.                         if pl_obj.has_key("url") or True:
  147.                             final_url += " swfurl=" + pl_obj["url"] + " swfvfy=1"
  148.  
  149.                         playpath = False
  150.                         if final_url.find("stream=") > -1:
  151.                             playpath = final_url[final_url.find("stream=")+7:]
  152.                             if playpath.find("&") > -1:
  153.                                 playpath = playpath[:playpath.find("&")]
  154.                         else:
  155.                             playpath = fmt_url_map[index - 1]
  156.  
  157.                         if playpath:
  158.                             if pl_obj["args"].has_key("ptk") and pl_obj["args"].has_key("ptchn"):
  159.                                 final_url += " playpath=" + playpath + "?ptchn=" + pl_obj["args"]["ptchn"] + "&ptk=" + pl_obj["args"]["ptk"]
  160.  
  161.                     links[int(quality)] = final_url.replace('\/','/')
  162.        
  163.         if self.__dbg__:
  164.             print self.__plugin__ + " getVideoUrlMap done " + repr(links)
  165.         return links
  166.            
  167.     def getInfo(self, params):
  168.         get = params.get
  169.         video = {}
  170.        
  171.         (result, status) = core._fetchPage(link = self.urls["video_info"] % get("videoid"), api=True)
  172.  
  173.         if status == 200:
  174.             video = core._getvideoinfo(result)
  175.        
  176.             if len(result) == 0:
  177.                 if self.__dbg__:
  178.                     print self.__plugin__ + " Couldn't parse API output, YouTube doesn't seem to know this video id?"
  179.                 video["apierror"] = self.__language__(30603)
  180.                 return (video, 303)
  181.         else:
  182.             if self.__dbg__:
  183.                 print self.__plugin__ + " Got API Error from YouTube!"
  184.             video["apierror"] = result
  185.            
  186.             return (video,303)
  187.         video = video[0]
  188.         return (video, status)
  189.    
  190.     def selectVideoQuality(self, links, params):
  191.         get = params.get
  192.         link = links.get
  193.         video_url = ""
  194.  
  195.         if self.__dbg__:
  196.             print self.__plugin__ + " selectVideoQuality : " #+ repr(links)
  197.        
  198.         if get("action") == "download":
  199.             hd_quality = int(self.__settings__.getSetting( "hd_videos_download" ))
  200.             if ( hd_quality == 0 ):
  201.                 hd_quality = int(self.__settings__.getSetting( "hd_videos" ))
  202.             else:
  203.                 hd_quality -= 1
  204.         else:
  205.             if (not get("quality")):
  206.                 hd_quality = int(self.__settings__.getSetting( "hd_videos" ))
  207.             else:
  208.                 if (get("quality") == "1080p"):
  209.                     hd_quality = 2
  210.                 elif (get("quality") == "720p"):
  211.                     hd_quality = 1
  212.                 else:
  213.                     hd_quality = 0
  214.        
  215.             # SD videos are default, but we go for the highest res
  216.         if (link(35)):
  217.             video_url = link(35)
  218.         elif (link(34)):
  219.             video_url = link(34)
  220.         elif (link(59)): #<-- 480 for rtmpe
  221.             video_url = link(59)
  222.         elif (link(78)): #<-- seems to be around 400 for rtmpe
  223.             video_url = link(78)
  224.         elif (link(18)):
  225.             video_url = link(18)
  226.         elif (link(5)):
  227.             video_url = link(5)
  228.        
  229.         if (hd_quality > 0): #<-- 720p
  230.             if (link(22)):
  231.                 video_url = link(22)
  232.         if (hd_quality > 1): #<-- 1080p
  233.             if (link(18)):
  234.                 video_url = link(18)
  235.                
  236.         if not len(video_url) > 0:
  237.             if self.__dbg__:
  238.                 print self.__plugin__ + " selectVideoQuality - construct_video_url failed, video_url not set"
  239.             return video_url
  240.        
  241.         if get("action") != "download":
  242.             video_url += " | " + self.USERAGENT
  243.  
  244.         if self.__dbg__:
  245.             print self.__plugin__ + " selectVideoQuality done"         
  246.         return video_url
  247.    
  248.     def getVideoObject(self, params):
  249.         get = params.get
  250.         video = {}
  251.         links = []
  252.                
  253.         (video, status) = self.getInfo(params)
  254.         (links, video) = self._getVideoLinks(video, params)
  255.        
  256.         if links:
  257.             video["video_url"] = self.selectVideoQuality(links, params)
  258.             if video["video_url"] == "":
  259.                 video['apierror'] = self.__language__(30618)
  260.                 status = 303
  261.         else:
  262.             status = 303
  263.             vget = video.get
  264.             if vget("live_play"):
  265.                 video['apierror'] = self.__language__(30612)
  266.             elif vget("stream_map"):
  267.                 video['apierror'] = self.__language__(30620)
  268.             else:
  269.                 video['apierror'] = self.__language__(30618)
  270.        
  271.         return (video, status)
  272.  
  273.     def _convertFlashVars(self, html):
  274.         obj = { "PLAYER_CONFIG": { "args": {} } }
  275.         temp = html.split("&")
  276.         for item in temp:
  277.             it = item.split("=")
  278.             obj["PLAYER_CONFIG"]["args"][it[0]] = urllib.unquote_plus(it[1])
  279.         return obj
  280.  
  281.     def _getVideoLinks(self, video, params):
  282.         get = params.get
  283.         vget = video.get
  284.         player_object = {}
  285.         links = []
  286.  
  287.         if self.__dbg__:
  288.             print self.__plugin__ + " _getVideoLinks trying website"
  289.  
  290.         (result, status) = core._fetchPage(link = self.urls["video_stream"] % get("videoid"))
  291.  
  292.         if status == 200:
  293.             start = result.find("yt.playerConfig = ")
  294.  
  295.             if start > -1:
  296.                 start = start + len("yt.playerConfig = ")
  297.                 end = result.find("};", start) + 1
  298.                 data = result[start: end]
  299.                 if len(data) > 0:
  300.                     data = data.replace("\\/", "/")
  301.                     player_object = json.loads('{ "PLAYER_CONFIG" : ' + data + "}" )
  302.             else:
  303.                 data = result[result.find('flashvars'):].replace("\n", "").replace("\u0026","&").replace("&amp;", "&").replace('\\"','"')
  304.                 data = re.findall('flashvars="(.*?)"', data)
  305.                 if len(data) > 0:
  306.                     print repr(data[0])
  307.                     player_object = self._convertFlashVars(data[0])
  308.  
  309.         else:
  310.             # Default error reporting.
  311.             if status == 403:
  312.                 video['apierror'] = self.__language__(30617)
  313.             elif status != 200:
  314.                 if not vget('apierror'):
  315.                     video['apierror'] = self.__language__(30617)
  316.  
  317.             if self.__dbg__:
  318.                 print self.__plugin__ + " _getVideoLinks Falling back to embed"
  319.  
  320.             (result, status) = core._fetchPage(link = self.urls["embed_stream"] % get("videoid"))
  321.        
  322.             # Fallback error reporting
  323.             if result.find("status=fail") > -1:
  324.                 status = 303
  325.                 video["apierror"] = re.compile('reason=(.*)%3Cbr').findall(content)[0]
  326.  
  327.             if status == 200:
  328.                 player_object = self._convertFlashVars(result)
  329.  
  330.         # Find playback URI
  331.         if player_object.has_key("PLAYER_CONFIG"):
  332.             if player_object["PLAYER_CONFIG"].has_key("args"):
  333.                 if player_object["PLAYER_CONFIG"]["args"].has_key("ttsurl"):
  334.                     video["ttsurl"] = player_object["PLAYER_CONFIG"]["args"]["ttsurl"]
  335.  
  336.                 links = self.getVideoUrlMap(player_object["PLAYER_CONFIG"], video)
  337.  
  338.                 if len(links) == 0:
  339.                     if self.__dbg__:
  340.                         print self.__plugin__ + " _getVideoLinks Couldn't find url map or stream map."
  341.  
  342.         return (links, video)
  343.    
  344.     def makeAscii(self, str):
  345.         try:
  346.             return str.encode('ascii')
  347.         except:
  348.             if self.__dbg__:
  349.                 print self.__plugin__ + " makeAscii hit except on : " + repr(str)
  350.             s = ""
  351.             for i in str:
  352.                 try:
  353.                     i.encode("ascii")
  354.                 except:
  355.                     continue
  356.                 else:
  357.                     s += i
  358.             return s
  359.  
  360.     # Standardised error handler
  361.     def showErrorMessage(self, title = "", result = "", status = 500):
  362.         if title == "":
  363.             title = self.__language__(30600)
  364.         if result == "":
  365.             result = self.__language__(30617)
  366.            
  367.         if ( status == 303):
  368.             self.showMessage(title, result)
  369.         else :
  370.             self.showMessage(title, self.__language__(30617))
  371.  
  372.     def showMessage(self, heading, message):
  373.         duration = ([5, 10, 15, 20, 25, 30][int(self.__settings__.getSetting( 'notification_length' ))]) * 1000
  374.         xbmc.executebuiltin('XBMC.Notification("%s", "%s", %s)' % ( heading, message, duration) )
Advertisement
Add Comment
Please, Sign In to add comment