Kodi plugin to to play various online streams (mostly Latvian)

context_download.py 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import sys, os, urllib2, re, requests
  2. #CLI_MODE = True
  3. from kodiswift import xbmc, xbmcgui, CLI_MODE
  4. from kodiswift import Plugin, storage
  5. from resources.lib.content import util, ContentSources
  6. #from resources.lib.content import Downloader
  7. #from twisted.web import client
  8. #from twisted.internet import reactor, defer
  9. #plugin = Plugin()
  10. #plugin.load_addon_settings()
  11. #playlist = plugin.get_setting("general_playlist",str)
  12. #proxy_url = plugin.get_setting("general_proxy_url",str)
  13. cunicode = lambda s: s.decode("utf8") if isinstance(s, str) else s
  14. cstr = lambda s: s.encode("utf8") if isinstance(s, unicode) else s
  15. cmd = sys.argv[1]
  16. title = sys.argv[2]
  17. data = sys.argv[3]
  18. download_dir = sys.argv[4]
  19. cur_directory = os.path.dirname(__file__)
  20. sources_directory = os.path.join(cur_directory,"resources","lib", "content", "sources")
  21. sources = ContentSources.ContentSources(sources_directory)
  22. def main():
  23. if not sources.is_video(data):
  24. print "It is not video link"
  25. notify("It is not video link")
  26. sys.exit(1)
  27. streams = sources.get_streams(data)
  28. if not CLI_MODE:
  29. ret = 0
  30. #ret = xbmcgui.Dialog().select("Select stream",streams) # TODO
  31. else:
  32. ret = 0
  33. stream = streams[ret]
  34. #output = stream["name"].replace("\\"," ").replace(":"," ").replace("|"," ")
  35. output = re.sub("[\\/\n\r\t,:\?\|'~\.]","_",title)
  36. if isinstance(output, str):
  37. output = output.decode("utf8")
  38. for sub in stream["subs"]:
  39. suburl = sub["url"]
  40. slang = "_" + sub["lang"] if sub["lang"] else ""
  41. download_sub(suburl, output+slang)
  42. if "nfo" in stream and stream["nfo"]:
  43. nfofile = os.path.join(download_dir, output+".nfo")
  44. with open(nfofile,"w") as f:
  45. nfo_txt = util.nfo2xml(stream["nfo"])
  46. f.write(nfo_txt)
  47. download_video(stream["url"], os.path.join(download_dir, output), stream["headers"])
  48. #d = Downloader.download_video(stream["url"], os.path.join(download_dir, output), stream["headers"])
  49. #reactor.run()
  50. #xbmcgui.Dialog().ok("Info","Start download")
  51. #mode = "a" if os.path.exists("context_menu.log") else "w"
  52. #with open("context_menu.log", mode) as f:
  53. # f.write("%s %s %s %s", sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
  54. def download_video(url,output,headers=None):
  55. #output = stream["name"].replace("\\"," ").replace(":"," ").replace("|"," ")
  56. if not headers:
  57. headers = {"user-agent":"Enigma2"}
  58. try:
  59. h = get_header(url,headers=headers)
  60. mtype = h.get("content-type")
  61. ext,stream_type = get_ext(mtype)
  62. except Exception as e:
  63. ext,stream_type = (".ts","hls")
  64. output = output+ext
  65. if stream_type == "hls":
  66. download_hls(url, output, headers=headers)
  67. else:
  68. download_file(url, output, headers=headers)
  69. def download_hls(url, title, download_dir="", headers=None, overwrite=True, limit=None):
  70. UA = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0"
  71. if not headers:
  72. headers = {"User-Agent" : UA}
  73. key = headers["key"] if "key" in headers else ""
  74. # if not "User-Agent" in headers:
  75. # headers["User-Agent"] = UA
  76. tsname = os.path.join(download_dir,title)
  77. notify("Download started - %s" % title)
  78. print "Start download"
  79. print url
  80. try:
  81. r = requests.get(url,headers=headers)
  82. except Exception as e:
  83. raise Exception("Cannot open manifsest file - %s"%url)
  84. if not r.content.startswith("#EXTM3U"):
  85. raise Exception("Not valid manifest file - %s" % url)
  86. streams = re.findall(r"#EXT-X-STREAM-INF:.*?BANDWIDTH=(\d+).*?\n(.+?)$", r.content, re.IGNORECASE | re.MULTILINE)
  87. i = 0
  88. while streams:
  89. if i > 4: break
  90. sorted(streams, key=lambda item: int(item[0]), reverse=True)
  91. base_url = "/".join(url.split("?")[0].split("/")[:-1])+"/"
  92. url = streams[0][1]
  93. if not url.startswith("http"):
  94. url = base_url + url
  95. print url
  96. try:
  97. r = requests.get(url, headers=headers)
  98. except Exception as e:
  99. raise Exception("Cannot open manifsest file - %s"%url)
  100. i += 1
  101. streams = re.findall(r"#EXT-X-STREAM-INF:.*?BANDWIDTH=(\d+).*?\n(.+?)$", r.content, re.IGNORECASE | re.MULTILINE)
  102. ts_list = re.findall(r"#EXTINF:([\d\.]+),.*?\n(.+?)$", r.content, re.IGNORECASE | re.MULTILINE)
  103. base_url = "/".join(url.split("/")[:-1])+"/"
  104. if not len(ts_list):
  105. raise Exception("Cannot read fragment list in manifsest file - %s"%url)
  106. ts_num = 0
  107. type = "vod" if "#EXT-X-ENDLIST" in r.content else "live"
  108. currentbytes = 0.0
  109. totalbytes = -1
  110. currenttime = 0.0
  111. totaltime = sum(map(float,zip(*ts_list)[0]))
  112. #ts_file = open(outputfile, "wb")
  113. if isinstance(tsname, str):
  114. tsname = tsname.decode("utf8")
  115. tsfile = open(tsname,"wb")
  116. for ts in ts_list:
  117. url2 = ts[1]
  118. #print "Downloading ", url2
  119. #fname = os.path.join(download_dir,url2.split("/")[-1])
  120. if not url2.startswith("http"):
  121. url2 = base_url + url2
  122. r = requests.get(url2, headers=headers, verify=False)
  123. content = r.content
  124. if key:
  125. from Crypto.Cipher import AES
  126. key2 = binascii.a2b_hex(key)
  127. iv = content[:16]
  128. d = AES.new(key2, AES.MODE_CBC, iv)
  129. content = d.decrypt(content[16:])
  130. #with open(fname,"wb") as f:
  131. #f.write(content)
  132. tsfile.write(content)
  133. content_length = len(content)
  134. currentbytes += content_length
  135. currenttime += float(ts_list[ts_num][0])
  136. totalbytes = currentbytes * totaltime / currenttime
  137. ts_num += 1
  138. #print "Fragment %s downloaded (%s)"%(self.ts_num,len(content))
  139. progress = float(currentbytes)/float(totalbytes)*100
  140. print "%.1f%% (%i/%i)"%(progress,currentbytes,totalbytes)
  141. if type == "vod":
  142. if ts_num >= len(ts_list) or (limit and currenttime>limit):
  143. break
  144. else:
  145. if limit and currenttime>limit: # TODO
  146. break
  147. print "Finished"
  148. notify("Download finished - %s" % title)
  149. tsfile.close()
  150. def download_file(url, title, download_dir="", headers=None, overwrite=True, limit=None):
  151. UA = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0"
  152. if not headers:
  153. headers = {"User-Agent" : UA}
  154. key = headers["key"] if "key" in headers else ""
  155. # if not "User-Agent" in headers:
  156. # headers["User-Agent"] = UA
  157. fname = os.path.join(download_dir,title)
  158. if isinstance(fname, str):
  159. fname = fname.decode("utf8")
  160. notify("Download started - %s" % title)
  161. print "Start download"
  162. print url
  163. try:
  164. r = requests.get(url,headers=headers, stream=True)
  165. except Exception as e:
  166. raise Exception("Cannot open url - %s"%url)
  167. currentbytes = 0.0
  168. totalbytes = int(r.headers["content-length"])
  169. with open(fname, 'wb') as fd:
  170. for chunk in r.iter_content(chunk_size=1024*1024):
  171. fd.write(chunk)
  172. currentbytes += len(chunk)
  173. progress = float(currentbytes)/float(totalbytes)*100
  174. print "%.1f%% (%i/%i)"%(progress,currentbytes,totalbytes)
  175. print "Finished"
  176. notify("Download finished - %s" % title)
  177. def download_sub(suburl, output):
  178. try:
  179. subs = urllib2.urlopen(suburl).read()
  180. except:
  181. subs = None
  182. if subs:
  183. if ".xml" in suburl:
  184. subs = util.ttaf2srt(subs)
  185. subext = ".srt"
  186. elif ".vtt" in suburl:
  187. subext = ".vtt"
  188. elif ".srt" in suburl:
  189. subext = ".srt"
  190. else:
  191. subext = ""
  192. if subext:
  193. subfile = cunicode(os.path.join(download_dir, output+subext))
  194. with open(subfile,"w") as f:
  195. f.write(subs)
  196. else:
  197. print "\n Error downloading subtitle %s"%suburl
  198. def get_header(url,headers=None):
  199. r = requests.head(url,headers=headers)
  200. return r.headers
  201. def get_ext(mtype):
  202. stype = "http"
  203. if mtype in ("vnd.apple.mpegURL","application/x-mpegURL",'application/x-mpegurl',"application/vnd.apple.mpegurl"):
  204. return ".ts","hls"
  205. elif mtype in ("application/dash+xml"):
  206. return ".ts","dash" # TODO dash stream type could be different !
  207. elif mtype in ("video/mp4"):
  208. return ".mp4","http"
  209. elif mtype in ("video/MP2T","video/mp2t"):
  210. return ".ts","http"
  211. elif mtype in ("video/x-flv"):
  212. return ".flv","http"
  213. elif mtype in ("video/quicktime"):
  214. return ".mov","http"
  215. elif mtype in ("video/x-msvideo"):
  216. return ".avi","http"
  217. elif mtype in ("video/x-ms-wmv"):
  218. return ".wmv","http"
  219. elif mtype in ("video/x-matroska"):
  220. return ".mkv","http"
  221. else:
  222. return ".mp4","http"
  223. def notify(text, title="Info", time=10000):
  224. if isinstance(text, unicode):
  225. text = text.encode("utf8")
  226. #xbmc.executebuiltin('Notification(Hello World,This is a simple example of notifications,5000,/script.hellow.world.png)')
  227. xbmc.executebuiltin('Notification(%s, %s, %d, %s)'%("Info", text, time, xbmcgui.NOTIFICATION_INFO))
  228. if __name__ == '__main__':
  229. main()