Advertisement
Don_black

youtubeplugin.py file

Aug 11th, 2012
203
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 36.08 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
  20. import urllib
  21. import re
  22. import os.path
  23. import time
  24. try: import simplejson as json
  25. except ImportError: import json
  26.  
  27.  
  28. class YouTubePlayer():
  29. fmt_value = {
  30. 5: "240p h263 flv container",
  31. 18: "360p h264 mp4 container | 270 for rtmpe?",
  32. 22: "720p h264 mp4 container",
  33. 26: "???",
  34. 33: "???",
  35. 34: "360p h264 flv container",
  36. 35: "480p h264 flv container",
  37. 37: "1080p h264 mp4 container",
  38. 38: "720p vp8 webm container",
  39. 43: "360p h264 flv container",
  40. 44: "480p vp8 webm container",
  41. 45: "720p vp8 webm container",
  42. 46: "520p vp8 webm stereo",
  43. 59: "480 for rtmpe",
  44. 78: "seems to be around 400 for rtmpe",
  45. 82: "360p h264 stereo",
  46. 83: "240p h264 stereo",
  47. 84: "720p h264 stereo",
  48. 85: "520p h264 stereo",
  49. 100: "360p vp8 webm stereo",
  50. 101: "480p vp8 webm stereo",
  51. 102: "720p vp8 webm stereo",
  52. 120: "hd720",
  53. 121: "hd1080"
  54. }
  55.  
  56. # YouTube Playback Feeds
  57. urls = {}
  58. urls['video_stream'] = "http://www.youtube.com/watch?v=%s&safeSearch=none"
  59. urls['embed_stream'] = "http://www.youtube.com/get_video_info?video_id=%s"
  60. urls['timed_text_index'] = "http://www.youtube.com/api/timedtext?type=list&v=%s"
  61. urls['video_info'] = "http://gdata.youtube.com/feeds/api/videos/%s"
  62. urls['close_caption_url'] = "http://www.youtube.com/api/timedtext?type=track&v=%s&lang=%s"
  63. 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"
  64. urls['annotation_url'] = "http://www.youtube.com/annotations/read2?video_id=%s&feat=TC"
  65. urls['remove_watch_later'] = "http://www.youtube.com/addto_ajax?action_delete_from_playlist=1"
  66.  
  67. def __init__(self):
  68. self.xbmc = sys.modules["__main__"].xbmc
  69. self.xbmcgui = sys.modules["__main__"].xbmcgui
  70. self.xbmcplugin = sys.modules["__main__"].xbmcplugin
  71. self.xbmcvfs = sys.modules["__main__"].xbmcvfs
  72.  
  73. self.settings = sys.modules["__main__"].settings
  74. self.language = sys.modules["__main__"].language
  75. self.plugin = sys.modules["__main__"].plugin
  76. self.dbg = sys.modules["__main__"].dbg
  77.  
  78. self.common = sys.modules["__main__"].common
  79. self.utils = sys.modules["__main__"].utils
  80. self.cache = sys.modules["__main__"].cache
  81. self.core = sys.modules["__main__"].core
  82.  
  83. self.login = sys.modules["__main__"].login
  84. self.feeds = sys.modules["__main__"].feeds
  85. self.storage = sys.modules["__main__"].storage
  86. self.scraper = sys.modules["__main__"].scraper
  87.  
  88. # ================================ Subtitle Downloader ====================================
  89. def downloadSubtitle(self, video={}):
  90. self.common.log("")
  91. get = video.get
  92.  
  93. style = ""
  94. result = ""
  95.  
  96. if self.settings.getSetting("annotations") == "true" and not "downloadPath" in video:
  97. xml = self.core._fetchPage({"link": self.urls["annotation_url"] % get('videoid')})
  98. if xml["status"] == 200 and xml["content"]:
  99. (result, style) = self.transformAnnotationToSSA(xml["content"])
  100.  
  101. if self.settings.getSetting("lang_code") != "0":
  102. subtitle_url = self.getSubtitleUrl(video)
  103.  
  104. if not subtitle_url and self.settings.getSetting("transcode") == "true":
  105. subtitle_url = self.getTranscriptionUrl(video)
  106.  
  107. if subtitle_url:
  108. xml = self.core._fetchPage({"link": subtitle_url})
  109. if xml["status"] == 200 and xml["content"]:
  110. result += self.transformSubtitleXMLtoSRT(xml["content"])
  111.  
  112. if len(result) > 0:
  113. result = "[Script Info]\r\n; This is a Sub Station Alpha v4 script.\r\n; For Sub Station Alpha info and downloads,\r\n; go to http://www.eswat.demon.co.uk/\r\n; or email kotus@eswat.demon.co.uk\r\nTitle: Auto Generated\r\nScriptType: v4.00\r\nCollisions: Normal\r\nPlayResY: 1280\r\nPlayResX: 800\r\n\r\n[V4 Styles]\r\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding\r\nStyle: Default,Arial,80,&H00FFFFFF&,65535,65535,&00000000&,-1,0,1,3,2,2,0,0,0,0,0\r\nStyle: speech,Arial,60,0,65535,65535,&H4BFFFFFF&,0,0,3,1,0,1,0,0,0,0,0\r\nStyle: popup,Arial,60,0,65535,65535,&H4BFFFFFF&,0,0,3,3,0,1,0,0,0,0,0\r\nStyle: highlightText,Wolf_Rain,60,15724527,15724527,15724527,&H4BFFFFFF&,0,0,1,1,2,2,5,5,0,0,0\r\n" + style + "\r\n[Events]\r\nFormat: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n" + result
  114.  
  115. result += "Dialogue: Marked=0,0:00:0.00,0:00:0.00,Default,Name,0000,0000,0000,,\r\n" # This solves a bug.
  116. self.saveSubtitle(result, video)
  117. self.common.log("Done")
  118. return True
  119.  
  120. self.common.log("Failure")
  121. return False
  122.  
  123. def getSubtitleUrl(self, video={}):
  124. self.common.log("")
  125. get = video.get
  126. url = ""
  127.  
  128. xml = self.core._fetchPage({"link": self.urls["timed_text_index"] % get('videoid')})
  129.  
  130. self.common.log("subtitle index: " + repr(xml["content"]))
  131.  
  132. if xml["status"] == 200:
  133. subtitle = ""
  134. code = ""
  135. codelist = self.common.parseDOM(xml["content"], "track", ret="lang_code")
  136. sublist = self.common.parseDOM(xml["content"], "track", ret="name")
  137. if len(sublist) != len(codelist):
  138. self.common.log("Code list and sublist length mismatch: " + repr(codelist) + " - " + repr(sublist))
  139. return ""
  140.  
  141. if len(codelist) > 0:
  142. # Fallback to first in list.
  143. subtitle = sublist[0].replace(" ", "%20")
  144. code = codelist[0]
  145.  
  146. lang_code = ["off", "en", "es", "de", "fr", "it", "ja"][int(self.settings.getSetting("lang_code"))]
  147. self.common.log("selected language: " + repr(lang_code))
  148. for i in range(0, len(codelist)):
  149. data = codelist[i].lower()
  150. if data.find("-") > -1:
  151. data = data[:data.find("-")]
  152.  
  153. if codelist[i].find(lang_code) > -1:
  154. subtitle = sublist[i].replace(" ", "%20")
  155. code = codelist[i]
  156. self.common.log("found subtitle specified: " + subtitle + " - " + code)
  157. break
  158.  
  159. if codelist[i].find("en") > -1:
  160. subtitle = sublist[i].replace(" ", "%20")
  161. code = "en"
  162. self.common.log("found subtitle default: " + subtitle + " - " + code)
  163.  
  164.  
  165. if code:
  166. url = self.urls["close_caption_url"] % (get("videoid"), code)
  167. if len(subtitle) > 0:
  168. url += "&name=" + subtitle
  169.  
  170.  
  171. self.common.log("found subtitle url: " + repr(url))
  172. return url
  173.  
  174. def getSubtitleFileName(self, video):
  175. get = video.get
  176. lang_code = ["off", "en", "es", "de", "fr", "it", "ja"][int(self.settings.getSetting("lang_code"))]
  177. filename = ''.join(c for c in self.common.makeUTF8(video['Title']) if c not in self.utils.INVALID_CHARS) + "-[" + get('videoid') + "]-" + lang_code.upper() + ".ssa"
  178. filename = filename.encode("ascii", "ignore")
  179. return filename
  180.  
  181. def saveSubtitle(self, result, video={}):
  182. self.common.log("")
  183. filename = self.getSubtitleFileName(video)
  184.  
  185. path = os.path.join(self.xbmc.translatePath(self.settings.getAddonInfo("profile")).decode("utf-8"), filename)
  186.  
  187. w = self.storage.openFile(path, "wb")
  188. w.write(self.utils.convertStringToBinary(result))
  189. w.close()
  190.  
  191. if "downloadPath" in video:
  192. self.xbmcvfs.rename(path, os.path.join(video["downloadPath"], filename))
  193.  
  194.  
  195. def getTranscriptionUrl(self, video={}):
  196. self.common.log("")
  197. get = video.get
  198. trans_url = ""
  199. if "ttsurl" in video:
  200. if len(video["ttsurl"]) > 0:
  201. trans_url = urllib.unquote(video["ttsurl"]).replace("\\", "") + "&type=trackformat=1&lang=en&kind=asr&name=&v=" + get("videoid")
  202. if self.settings.getSetting("lang_code") > 1: # 1 == en
  203. lang_code = ["off", "en", "es", "de", "fr", "it", "ja"][int(self.settings.getSetting("lang_code"))]
  204. trans_url += "&tlang=" + lang_code
  205. return trans_url
  206.  
  207. def simpleReplaceHTMLCodes(self, str):
  208. str = str.strip()
  209. str = str.replace("&amp;", "&")
  210. str = str.replace("&quot;", '"')
  211. str = str.replace("&hellip;", "...")
  212. str = str.replace("&gt;", ">")
  213. str = str.replace("&lt;", "<")
  214. str = str.replace("&#39;", "'")
  215. return str
  216.  
  217. def convertSecondsToTimestamp(self, seconds):
  218. self.common.log("", 3)
  219. hours = str(int(seconds / 3600))
  220. seconds = seconds % 3600
  221.  
  222. minutes = str(int(seconds/60))
  223. if len(minutes) == 1:
  224. minutes = "0" + minutes
  225.  
  226. seconds = str(seconds % 60)
  227. if len(seconds) == 1:
  228. seconds = "0" + seconds
  229.  
  230. self.common.log("Done", 3)
  231. return "%s:%s:%s" % (hours, minutes, seconds)
  232.  
  233. def transformSubtitleXMLtoSRT(self, xml):
  234. self.common.log("")
  235.  
  236. result = ""
  237. for node in self.common.parseDOM(xml, "text", ret=True):
  238. text = self.common.parseDOM(node, "text")[0]
  239. text = self.simpleReplaceHTMLCodes(text).replace("\n", "\\n")
  240. start = float(self.common.parseDOM(node, "text", ret="start")[0])
  241. duration = self.common.parseDOM(node, "text", ret="dur")
  242. end = start + 0.1
  243. if len(duration) > 0:
  244. end = start + float(duration[0])
  245.  
  246. start = self.convertSecondsToTimestamp(start)
  247. end = self.convertSecondsToTimestamp(end)
  248.  
  249. if start and end:
  250. result += "Dialogue: Marked=%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\r\n" % ("0", start, end, "Default", "Name", "0000", "0000", "0000", "", text)
  251.  
  252. return result
  253.  
  254. def transformColor(self, color):
  255. self.common.log("Color: %s - len: %s" % (color, len(color)), 3)
  256. if color:
  257. color = hex(int(color))
  258. color = str(color)
  259. color = color[2:]
  260. self.common.log("Color: %s - len: %s" % (color, len(color)), 5)
  261. if color == "0":
  262. color = "000000"
  263. if len(color) == 4:
  264. color = "00" + color
  265. if len(color) == 6:
  266. color = color[4:6] + color[2:4] + color[0:2]
  267.  
  268. self.common.log("Returning color: %s - len: %s" % (color, len(color)), 5)
  269. return color
  270.  
  271. def transformAlpha(self, alpha):
  272. self.common.log("Alpha: %s - len: %s" % (alpha, len(alpha)), 5)
  273. if not alpha or alpha == "0" or alpha == "0.0":
  274. alpha = "-1" # No background.
  275. else:
  276. # YouTube and SSA have inverted alphas.
  277. alpha = int(float(alpha) * 256)
  278. alpha = hex(256 - alpha)
  279. alpha = alpha[2:]
  280.  
  281. self.common.log("Alpha: %s - len: %s" % (alpha, len(alpha)), 5)
  282. return alpha
  283.  
  284. def transformAnnotationToSSA(self, xml):
  285. self.common.log("")
  286. result = ""
  287. ssa_fixes = []
  288. style_template = "Style: annot%s,Arial,%s,&H%s&,&H%s&,&H%s&,&H%s&,0,0,3,3,0,1,0,0,0,0,0\r\n"
  289. styles_count = 0
  290. append_style = ""
  291. entries = self.common.parseDOM(xml, "annotation", ret=True)
  292. for node in entries:
  293. if node:
  294. stype = "".join(self.common.parseDOM(node, "annotation", ret="type"))
  295. style = "".join(self.common.parseDOM(node, "annotation", ret="style"))
  296. self.common.log("stype : " + stype, 5)
  297. self.common.log("style : " + style, 5)
  298.  
  299. if stype == "highlight":
  300. linkt = "".join(self.common.parseDOM(node, "url", ret="type"))
  301. linkv = "".join(self.common.parseDOM(node, "url", ret="value"))
  302. if linkt == "video":
  303. self.common.log("Reference to video : " + linkv)
  304. elif node.find("TEXT") > -1:
  305. text = self.common.parseDOM(node, "TEXT")
  306. if len(text):
  307. text = self.common.replaceHTMLCodes(text[0])
  308. start = ""
  309.  
  310. ns_fsize = 60
  311. start = False
  312. end = False
  313.  
  314. if style == "popup":
  315. cnode = self.common.parseDOM(node, "rectRegion", ret="t")
  316. start = cnode[0]
  317. end = cnode[1]
  318. tmp_y = self.common.parseDOM(node, "rectRegion", ret="y")
  319. tmp_h = self.common.parseDOM(node, "rectRegion", ret="h")
  320. tmp_x = self.common.parseDOM(node, "rectRegion", ret="x")
  321. tmp_w = self.common.parseDOM(node, "rectRegion", ret="w")
  322. elif style == "speech":
  323. cnode = self.common.parseDOM(node, "anchoredRegion", ret="t")
  324. start = cnode[0]
  325. end = cnode[1]
  326. tmp_y = self.common.parseDOM(node, "anchoredRegion", ret="y")
  327. tmp_h = self.common.parseDOM(node, "anchoredRegion", ret="h")
  328. tmp_x = self.common.parseDOM(node, "anchoredRegion", ret="x")
  329. tmp_w = self.common.parseDOM(node, "anchoredRegion", ret="w")
  330. elif style == "higlightText":
  331. cnode = False
  332. else:
  333. cnode = False
  334.  
  335. for snode in self.common.parseDOM(node, "appearance", attrs={"fgColor": ".*?"}, ret=True):
  336. ns_fsize = self.common.parseDOM(snode, "appearance", ret="textSize")
  337. if len(ns_fsize):
  338. ns_fsize = int(1.2 * (1280 * float(ns_fsize[0]) / 100))
  339. else:
  340. ns_fsize = 60
  341. ns_fcolor = self.common.parseDOM(snode, "appearance", ret="fgColor")
  342. ns_fcolor = self.transformColor(ns_fcolor[0])
  343.  
  344. ns_bcolor = self.common.parseDOM(snode, "appearance", ret="bgColor")
  345. ns_bcolor = self.transformColor(ns_bcolor[0])
  346.  
  347. ns_alpha = self.common.parseDOM(snode, "appearance", ret="bgAlpha")
  348. ns_alpha = self.transformAlpha(ns_alpha[0])
  349.  
  350. append_style += style_template % (styles_count, ns_fsize, ns_fcolor, ns_fcolor, ns_fcolor, ns_alpha + ns_bcolor)
  351. style = "annot" + str(styles_count)
  352. styles_count += 1
  353.  
  354. self.common.log("start: %s - end: %s - style: %s" % (start, end, style), 5)
  355. if start and end and style != "highlightText":
  356. marginV = 1280 * float(tmp_y[0]) / 100
  357. marginV += 1280 * float(tmp_h[0]) / 100
  358. marginV = 1280 - int(marginV)
  359. marginV += 5
  360. marginL = int((800 * float(tmp_x[0]) / 100))
  361. marginL += 5
  362. marginR = 800 - marginL - int((800 * float(tmp_w[0]) / 100)) - 15
  363. if marginR < 0:
  364. marginR = 0
  365. result += "Dialogue: Marked=%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\r\n" % ("0", start, end, style, "Name", marginL, marginR, marginV, "", text)
  366. ssa_fixes.append([start, end])
  367.  
  368. # Fix errors in the SSA specs.
  369. if len(ssa_fixes) > 0:
  370. for a_start, a_end in ssa_fixes:
  371. for b_start, b_end in ssa_fixes:
  372. if time.strptime(a_end[0:a_end.rfind(".")], "%H:%M:%S") < time.strptime(b_start[0:b_start.rfind(".")], "%H:%M:%S"):
  373. result += "Dialogue: Marked=0,%s,%s,Default,Name,0000,0000,0000,,\r\n" % (a_end, b_start)
  374.  
  375. self.common.log("Done : " + repr((result, append_style)),5)
  376. return (result, append_style)
  377.  
  378. def addSubtitles(self, video={}):
  379. get = video.get
  380. self.common.log("fetching subtitle if available")
  381.  
  382. filename = self.getSubtitleFileName(video)
  383.  
  384. download_path = os.path.join(self.settings.getSetting("downloadPath").decode("utf-8"), filename)
  385. path = os.path.join(self.xbmc.translatePath(self.settings.getAddonInfo("profile")).decode("utf-8"), filename)
  386.  
  387. set_subtitle = False
  388. if self.xbmcvfs.exists(download_path):
  389. path = download_path
  390. set_subtitle = True
  391. elif self.xbmcvfs.exists(path):
  392. set_subtitle = True
  393. elif self.downloadSubtitle(video):
  394. set_subtitle = True
  395.  
  396. self.common.log("Done trying to locate: " + path, 4)
  397. if self.xbmcvfs.exists(path) and not "downloadPath" in video and set_subtitle:
  398. player = self.xbmc.Player()
  399.  
  400. i = 0
  401. while not player.isPlaying():
  402. i += 1
  403. self.common.log("Waiting for playback to start ")
  404. time.sleep(1)
  405. if i > 10:
  406. break
  407.  
  408. self.xbmc.Player().setSubtitles(path)
  409. self.common.log("added subtitle %s to playback" % path)
  410.  
  411. # ================================ Video Playback ====================================
  412.  
  413. def playVideo(self, params={}):
  414. self.common.log(repr(params), 3)
  415. get = params.get
  416.  
  417. (video, status) = self.getVideoObject(params)
  418.  
  419. if status != 200:
  420. self.common.log("construct video url failed contents of video item " + repr(video))
  421. self.utils.showErrorMessage(self.language(30603), video["apierror"], status)
  422. return False
  423.  
  424. listitem = self.xbmcgui.ListItem(label=video['Title'], iconImage=video['thumbnail'], thumbnailImage=video['thumbnail'], path=video['video_url'])
  425.  
  426. listitem.setInfo(type='Video', infoLabels=video)
  427.  
  428. self.common.log("Playing video: " + repr(video['Title']) + " - " + repr(get('videoid')) + " - " + repr(video['video_url']))
  429.  
  430. self.xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), succeeded=True, listitem=listitem)
  431.  
  432. if self.settings.getSetting("lang_code") != "0" or self.settings.getSetting("annotations") == "true":
  433. self.addSubtitles(video)
  434.  
  435. if (get("watch_later") == "true" and get("playlist_entry_id")):
  436. self.common.log("removing video from watch later playlist")
  437. self.core.remove_from_watch_later(params)
  438.  
  439. self.storage.storeValue("vidstatus-" + video['videoid'], "7")
  440.  
  441. def getVideoUrlMap(self, pl_obj, video={}):
  442. self.common.log(repr(pl_obj))
  443. links = {}
  444. video["url_map"] = "true"
  445.  
  446. html = ""
  447. if "fmt_stream_map" in pl_obj["args"]:
  448. html = pl_obj["args"]["fmt_stream_map"]
  449.  
  450. if len(html) == 0 and "url_encoded_fmt_stream_map" in pl_obj["args"]:
  451. html = urllib.unquote(pl_obj["args"]["url_encoded_fmt_stream_map"])
  452.  
  453. if len(html) == 0 and"fmt_url_map" in pl_obj["args"]:
  454. html = pl_obj["args"]["fmt_url_map"]
  455.  
  456. html = urllib.unquote_plus(html)
  457.  
  458. if "liveplayback_module" in pl_obj["args"]:
  459. video["live_play"] = "true"
  460.  
  461. fmt_url_map = [html]
  462. if html.find("|") > -1:
  463. fmt_url_map = html.split('|')
  464. elif html.find(",url=") > -1:
  465. fmt_url_map = html.split(',url=')
  466. elif html.find("&conn=") > -1:
  467. video["stream_map"] = "true"
  468. fmt_url_map = html.split('&conn=')
  469.  
  470. if len(fmt_url_map) > 0:
  471. for index, fmt_url in enumerate(fmt_url_map):
  472. if fmt_url.find("&url") > -1:
  473. self.common.log("Searching for fmt_url_map : " + repr(fmt_url))
  474. fmt_url = fmt_url.split("&url")
  475. fmt_url_map += [fmt_url[1]]
  476. fmt_url = fmt_url[0]
  477.  
  478. if (len(fmt_url) > 7 and fmt_url.find("&") > 7):
  479. quality = "5"
  480. final_url = fmt_url.replace(" ", "%20").replace("url=", "")
  481.  
  482. if (final_url.rfind(';') > 0):
  483. final_url = final_url[:final_url.rfind(';')]
  484.  
  485. if (final_url.rfind(',') > final_url.rfind('&id=')):
  486. final_url = final_url[:final_url.rfind(',')]
  487. elif (final_url.rfind(',') > final_url.rfind('/id/') and final_url.rfind('/id/') > 0):
  488. final_url = final_url[:final_url.rfind('/')]
  489.  
  490. if (final_url.rfind('itag=') > 0):
  491. quality = final_url[final_url.rfind('itag=') + 5:]
  492. if quality.find('&') > -1:
  493. quality = quality[:quality.find('&')]
  494. if quality.find(',') > -1:
  495. quality = quality[:quality.find(',')]
  496. elif (final_url.rfind('/itag/') > 0):
  497. quality = final_url[final_url.rfind('/itag/') + 6:]
  498.  
  499. if final_url.find("&type") > 0:
  500. final_url = final_url[:final_url.find("&type")]
  501. if self.settings.getSetting("preferred") == "false":
  502. pos = final_url.find("://")
  503. fpos = final_url.find("fallback_host")
  504. if pos > -1 and fpos > -1:
  505. host = final_url[pos + 3:]
  506. if host.find("/") > -1:
  507. host = host[:host.find("/")]
  508. fmt_fallback = final_url[fpos + 14:]
  509. if fmt_fallback.find("&") > -1:
  510. fmt_fallback = fmt_fallback[:fmt_fallback.find("&")]
  511. self.common.log("Swapping cached host [%s] and fallback host [%s] " % (host, fmt_fallback), 5)
  512. final_url = final_url.replace(host, fmt_fallback)
  513. final_url = final_url.replace("fallback_host=" + fmt_fallback, "fallback_host=" + host)
  514.  
  515. # Extract RTMP variables
  516. if final_url.find("rtmp") > -1 and index > 0:
  517. if "url" in pl_obj:
  518. final_url += " swfurl=" + pl_obj["url"] + " swfvfy=1"
  519.  
  520. playpath = False
  521. if final_url.find("stream=") > -1:
  522. playpath = final_url[final_url.find("stream=") + 7:]
  523. if playpath.find("&") > -1:
  524. playpath = playpath[:playpath.find("&")]
  525. else:
  526. playpath = fmt_url_map[index - 1]
  527.  
  528. if playpath:
  529. if "ptk" in pl_obj["args"] and "ptchn" in pl_obj["args"]:
  530. final_url += " playpath=" + playpath + "?ptchn=" + pl_obj["args"]["ptchn"] + "&ptk=" + pl_obj["args"]["ptk"]
  531.  
  532. links[int(quality)] = final_url.replace('\/', '/')
  533.  
  534. self.common.log("done " + repr(links))
  535. return links
  536.  
  537. def getInfo(self, params):
  538. get = params.get
  539. video = self.cache.get("videoidcache" + get("videoid"))
  540. if len(video) > 0:
  541. self.common.log("returning cache ")
  542. return (eval(video), 200)
  543.  
  544. result = self.core._fetchPage({"link": self.urls["video_info"] % get("videoid"), "api": "true"})
  545.  
  546. if result["status"] == 200:
  547. video = self.core.getVideoInfo(result["content"], params)
  548.  
  549. if len(video) == 0:
  550. self.common.log("- Couldn't parse API output, YouTube doesn't seem to know this video id?")
  551. video = {}
  552. video["apierror"] = self.language(30608)
  553. return (video, 303)
  554. else:
  555. self.common.log("- Got API Error from YouTube!")
  556. video = {}
  557. video["apierror"] = result["content"]
  558.  
  559. return (video, 303)
  560.  
  561. video = video[0]
  562. self.cache.set("videoidcache" + get("videoid"), repr(video))
  563. return (video, result["status"])
  564.  
  565. def selectVideoQuality(self, links, params):
  566. get = params.get
  567. link = links.get
  568. video_url = ""
  569.  
  570. self.common.log("")
  571.  
  572. if get("action") == "download":
  573. hd_quality = int(self.settings.getSetting("hd_videos_download"))
  574. if (hd_quality == 0):
  575. hd_quality = int(self.settings.getSetting("hd_videos"))
  576.  
  577. else:
  578. if (not get("quality")):
  579. hd_quality = int(self.settings.getSetting("hd_videos"))
  580. else:
  581. if (get("quality") == "1080p"):
  582. hd_quality = 3
  583. elif (get("quality") == "720p"):
  584. hd_quality = 2
  585. else:
  586. hd_quality = 1
  587.  
  588. # SD videos are default, but we go for the highest res
  589. if (link(35)):
  590. video_url = link(35)
  591. elif (link(59)):
  592. video_url = link(59)
  593. elif link(44):
  594. video_url = link(44)
  595. elif (link(78)):
  596. video_url = link(78)
  597. elif (link(34)):
  598. video_url = link(34)
  599. elif (link(43)):
  600. video_url = link(43)
  601. elif (link(26)):
  602. video_url = link(26)
  603. elif (link(18)):
  604. video_url = link(18)
  605. elif (link(33)):
  606. video_url = link(33)
  607. elif (link(5)):
  608. video_url = link(5)
  609.  
  610. if hd_quality > 1: # <-- 720p
  611. if (link(22)):
  612. video_url = link(22)
  613. elif (link(45)):
  614. video_url = link(45)
  615. elif link(120):
  616. video_url = link(120)
  617. if hd_quality > 2:
  618. if (link(37)):
  619. video_url = link(37)
  620. elif link(121):
  621. video_url = link(121)
  622.  
  623. if link(38) and False:
  624. video_url = link(38)
  625.  
  626. for fmt_key in links.iterkeys():
  627. if link(int(fmt_key)):
  628. if self.dbg:
  629. text = repr(fmt_key) + " - "
  630. if fmt_key in self.fmt_value:
  631. text += self.fmt_value[fmt_key]
  632. else:
  633. text += "Unknown"
  634.  
  635. if (link(int(fmt_key)) == video_url):
  636. text += "*"
  637. self.common.log(text)
  638. else:
  639. self.common.log("- Missing fmt_value: " + repr(fmt_key))
  640.  
  641. if hd_quality == 0 and not get("quality"):
  642. return self.userSelectsVideoQuality(params, links)
  643.  
  644. if not len(video_url) > 0:
  645. self.common.log("- construct_video_url failed, video_url not set")
  646. return video_url
  647.  
  648. if get("proxy"):
  649. proxy = get("proxy")
  650. video_url = proxy + urllib.quote(video_url) + " |Referer=" + proxy[:proxy.rfind("/")]
  651.  
  652. if get("action") != "download":
  653. video_url += " | " + self.common.USERAGENT
  654.  
  655. self.common.log("Done")
  656. return video_url
  657.  
  658. def userSelectsVideoQuality(self, params, links):
  659. get = params.get
  660. link = links.get
  661. quality_list = []
  662. choices = []
  663.  
  664. if link(37):
  665. quality_list.append((37, "1080p"))
  666. elif link(121):
  667. quality_list.append((121, "1080p"))
  668.  
  669. if link(22):
  670. quality_list.append((22, "720p"))
  671. elif link(45):
  672. quality_list.append((45, "720p"))
  673. elif link(120):
  674. quality_list.append((120, "720p"))
  675.  
  676. if link(35):
  677. quality_list.append((35, "480p"))
  678. elif link(44):
  679. quality_list.append((44, "480p"))
  680.  
  681. if link(18):
  682. quality_list.append((18, "380p"))
  683.  
  684. if link(34):
  685. quality_list.append((34, "360p"))
  686. elif link(43):
  687. quality_list.append((43, "360p"))
  688.  
  689. if link(5):
  690. quality_list.append((5, "240p"))
  691. if link(17):
  692. quality_list.append((17, "144p"))
  693.  
  694. if link(38) and False:
  695. quality_list.append((37, "2304p"))
  696.  
  697. for (quality, message) in quality_list:
  698. choices.append(message)
  699.  
  700. dialog = self.xbmcgui.Dialog()
  701. selected = dialog.select(self.language(30518), choices)
  702.  
  703. if selected > -1:
  704. (quality, message) = quality_list[selected]
  705. return link(quality)
  706.  
  707. return ""
  708.  
  709. def getLocalFileSource(self, get, status, video):
  710. result = ""
  711. if (get("action", "") != "download"):
  712. path = self.settings.getSetting("downloadPath")
  713. filename = ''.join(c for c in self.common.makeUTF8(video['Title']) if c not in self.utils.INVALID_CHARS) + "-[" + get('videoid') + "]" + ".mp4"
  714. path = os.path.join(path.decode("utf-8"), filename)
  715. try:
  716. if self.xbmcvfs.exists(path):
  717. result = path
  718. except:
  719. self.common.log("failed to locate local subtitle file, trying youtube instead")
  720. return result
  721.  
  722. def getVideoObject(self, params):
  723. self.common.log(repr(params))
  724. get = params.get
  725.  
  726. links = []
  727. (video, status) = self.getInfo(params)
  728.  
  729. if status != 200:
  730. video['apierror'] = self.language(30618)
  731. return (video, 303)
  732.  
  733. video_url = self.getLocalFileSource(get, status, video)
  734. if video_url:
  735. video['video_url'] = video_url
  736. return (video, 200)
  737.  
  738. (links, video) = self._getVideoLinks(video, params)
  739.  
  740. if not links and self.settings.getSetting("proxy"):
  741. params["proxy"] = self.settings.getSetting("proxy")
  742. (links, video) = self._getVideoLinks(video, params)
  743.  
  744. if links:
  745. video["video_url"] = self.selectVideoQuality(links, params)
  746. if video["video_url"] == "":
  747. video['apierror'] = self.language(30618)
  748. status = 303
  749. else:
  750. status = 303
  751. vget = video.get
  752. if not "apierror" in video:
  753. if vget("live_play"):
  754. video['apierror'] = self.language(30612)
  755. elif vget("stream_map"):
  756. video['apierror'] = self.language(30620)
  757. else:
  758. video['apierror'] = self.language(30618)
  759.  
  760. self.common.log("Done : " + repr(status))
  761. return (video, status)
  762.  
  763. def _convertFlashVars(self, html):
  764. self.common.log(repr(html))
  765. obj = {"PLAYER_CONFIG": {"args": {}}}
  766. temp = html.split("&")
  767. for item in temp:
  768. self.common.log(item, 9)
  769. it = item.split("=")
  770. self.common.log(it, 9)
  771. obj["PLAYER_CONFIG"]["args"][it[0]] = urllib.unquote_plus(it[1])
  772. return obj
  773.  
  774. def _getVideoLinks(self, video, params):
  775. self.common.log("trying website: " + repr(params))
  776.  
  777. get = params.get
  778. player_object = {}
  779. links = []
  780. fresult = False
  781.  
  782. if get("proxy"):
  783. result = self.core._fetchPage({"link": self.urls["video_stream"] % get("videoid"), "proxy": get("proxy")})
  784. else:
  785. result = self.core._fetchPage({"link": self.urls["video_stream"] % get("videoid")})
  786.  
  787. if result["status"] == 200:
  788. start = result["content"].find("yt.playerConfig = ")
  789. if start > -1:
  790. self.common.log("Found player_config", 4)
  791. start = start + len("yt.playerConfig = ")
  792. end = result["content"].find("};", start) + 1
  793. data = result["content"][start: end]
  794. if len(data) > 0:
  795. data = data.replace("\\/", "/")
  796. player_object = json.loads('{ "PLAYER_CONFIG" : ' + data + "}" )
  797. self.common.log("player_object " + repr(player_object), 4)
  798. else:
  799. self.common.log("Using flashvars")
  800. data = result["content"].replace("\n", "").replace("\u0026", "&").replace("&amp;", "&").replace('\\"', '"')
  801. data = re.findall('flashvars="(.*?)"', data)
  802. src = self.common.parseDOM(result["content"], "embed", attrs={"id": "movie_player"}, ret="src")
  803. self.common.log(repr(data) + " - " + repr(src))
  804. if len(data) > 0 and len(src) > 0:
  805. self.common.log("Using flashvars converting", 4)
  806. data = data[0].replace("\n", "")
  807. player_object = self._convertFlashVars(data)
  808. if "PLAYER_CONFIG" in player_object:
  809. player_object["PLAYER_CONFIG"]["url"] = src[0]
  810.  
  811. elif get("no_embed", "false") == "false":
  812. self.common.log("Falling back to embed")
  813.  
  814. if get("proxy"):
  815. fresult = self.core._fetchPage({"link": self.urls["embed_stream"] % get("videoid"), "proxy": get("proxy")})
  816. else:
  817. fresult = self.core._fetchPage({"link": self.urls["embed_stream"] % get("videoid") })
  818.  
  819. # Fallback error reporting
  820. if fresult["content"].find("status=fail") > -1:
  821. fresult["status"] = 303
  822. error = fresult["content"]
  823. if error.find("reason=") > -1:
  824. error = error[error.find("reason=") + len("reason="):]
  825. if error.find("%3Cbr") > -1:
  826. error = error[:error.find("%3Cbr")]
  827. video["apierror"] = error.replace("+", " ")
  828.  
  829. if fresult["status"] == 200:
  830. # this gives no player_object["PLAYER_CONFIG"]["url"] for rtmpe...
  831. player_object = self._convertFlashVars(fresult["content"])
  832.  
  833. # Find playback URI
  834. if "PLAYER_CONFIG" in player_object:
  835. if "args" in player_object["PLAYER_CONFIG"]:
  836. if "ttsurl" in player_object["PLAYER_CONFIG"]["args"]:
  837. video["ttsurl"] = player_object["PLAYER_CONFIG"]["args"]["ttsurl"]
  838.  
  839. links = self.getVideoUrlMap(player_object["PLAYER_CONFIG"], video)
  840.  
  841. if len(links) == 0:
  842. self.common.log("Couldn't find url map or stream map.")
  843.  
  844. if not "apierror" in video:
  845. video['apierror'] = self.core._findErrors(result)
  846. if not video['apierror'] and fresult:
  847. video['apierror'] = self.core._findErrors(fresult)
  848.  
  849. self.common.log("Done")
  850. return (links, video)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement