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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. # -*- coding: utf-8 -*-
  2. import os,os.path,sys, glob, shutil, re
  3. import urllib, traceback
  4. try:
  5. import cPickle as pickle
  6. except:
  7. import pickle
  8. import pickle
  9. from kodiswift import Plugin, ListItem, storage
  10. from kodiswift import xbmc, xbmcgui, xbmcplugin, xbmcvfs, xbmcaddon, CLI_MODE
  11. from resources.lib.content import ContentSources, util
  12. try:
  13. import wingdbstub
  14. except:
  15. pass
  16. cur_directory = os.path.dirname(__file__)
  17. icon_folder = os.path.join(cur_directory, "resources", "picons")
  18. icong_url = xbmcaddon.Addon().getAddonInfo("path") + "/resources/picons/"
  19. sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),"resources","lib", "content", "sources"))
  20. plugin = Plugin()
  21. #plugin.load_addon_settings()
  22. use_storage = plugin.get_setting("general_use_storage",bool) # TODO vajag nočekot vai nav labāk lietot pickle
  23. storage_ttl = plugin.get_setting("general_ttl",int)
  24. use_proxy = plugin.get_setting("general_proxy_use",bool)
  25. proxy_url = plugin.get_setting("general_proxy_url",str)
  26. playlist = plugin.get_setting("general_playlist",str)
  27. download_dir = plugin.get_setting("general_download_dir",str)
  28. view_mode = plugin.get_setting("general_view_mode",str)
  29. streams_file = plugin.get_setting("general_streams_file",str)
  30. streams_file_remote = plugin.get_setting("general_streams_file_remote",str)
  31. use_streams_file_remote = plugin.get_setting("general_use_streams_file_remote",bool)
  32. #storage_path = os.path.join(plugin.storage_path,"sources.p")
  33. if use_storage:
  34. try:
  35. storage = plugin.get_storage("playstream","pickle",ttl=30)
  36. except Exception as e:
  37. os.remove(os.path.join(plugin.storage_path, "playstream"))
  38. try:
  39. storage = plugin.get_storage("playstream", "pickle", ttl=30)
  40. except Exception as e:
  41. print "[playstream] error opening storage", plugin.storage_path
  42. print "Got Exception: ", str(e)
  43. import traceback
  44. traceback.print_exc()
  45. plugin.notify("Error opening permament storage", "Info", 10000, xbmcgui.NOTIFICATION_INFO)
  46. storage = None
  47. os.remove(os.path.join(plugin.storage_path, "playstream"))
  48. cur_directory = os.path.dirname(__file__)
  49. sources_directory = os.path.join(cur_directory,"resources","lib", "content", "sources")
  50. cfg_list = glob.glob(os.path.join(sources_directory,"*.cfg"))
  51. for cf in cfg_list:
  52. cf2 = os.path.join(plugin.storage_path,os.path.split(cf)[1])
  53. if not os.path.exists(cf2):
  54. shutil.copyfile(cf,cf2)
  55. if use_storage and storage is not None and "sources" in storage:
  56. print "[playstream] Restore sources from storage"
  57. sources = storage["sources"]
  58. # if use_storage and os.path.exists(storage_path): #"sources" in storage:
  59. #sources = pickle.load(open(storage_path,"rb"))
  60. else:
  61. print "[playstream] Create sources objects"
  62. if use_streams_file_remote:
  63. try:
  64. sources = ContentSources.ContentSources(sources_directory, streams_file_remote)
  65. except Exception as e:
  66. try:
  67. sources = ContentSources.ContentSources(sources_directory, streams_file)
  68. plugin.notify("Remote streams file is not available, fallback to local")
  69. except Exception as e:
  70. plugin.notify(e.message)
  71. else:
  72. try:
  73. sources = ContentSources.ContentSources(sources_directory, streams_file)
  74. except Exception as e:
  75. plugin.notify(e.message)
  76. for source in sources.plugins:
  77. if not ("options" in dir(sources.plugins[source]) and sources.plugins[source].options): continue
  78. options = sources.plugins[source].options
  79. if not options: continue
  80. for option in options:
  81. key="%s_%s"%(source,option)
  82. if key in ("viaplay_device"): continue # exception list,
  83. value = plugin.get_setting(key)
  84. options[option] = value
  85. sources.plugins[source].options_write(options)
  86. prefix = ""
  87. @plugin.route(".+" )
  88. def main():
  89. global prefix
  90. prefix = "%s://%s/"%(plugin.request.scheme,plugin.request.netloc)
  91. plugin.set_content("movies")
  92. data = plugin.request.url.replace(prefix,"")
  93. data = urllib.unquote(data)
  94. sources.plugins["config"].read_streams()
  95. if not data:
  96. data = u"config::home"
  97. print "[playstream] processing data=%s"%data
  98. if sources.is_video(data):
  99. try:
  100. streams = sources.get_streams(data)
  101. except Exception,e:
  102. #xbmcgui.Dialog().ok("Error",unicode(e))
  103. plugin.notify(unicode(e),"Error",10000, xbmcgui.NOTIFICATION_ERROR)
  104. traceback.print_exc()
  105. return plugin.set_resolved_url(None)
  106. if streams:
  107. return play_video(streams)
  108. else:
  109. plugin.notify("No streams found!","Error",10000,xbmcgui.NOTIFICATION_ERROR)
  110. return plugin.set_resolved_url(None)
  111. else:
  112. if "{0}" in data:
  113. q = plugin.keyboard(default=None, heading="Search for", hidden=False)
  114. if isinstance(q,str):
  115. q = q.decode("utf8")
  116. if isinstance(data,str):
  117. data = data.decode("utf8")
  118. data = data.format(q)
  119. try:
  120. items = get_list(data)
  121. except Exception,e:
  122. plugin.notify(unicode(e),"Error",10000,xbmcgui.NOTIFICATION_ERROR)
  123. traceback.print_exc()
  124. return []
  125. if use_storage and storage is not None:
  126. print "[playstream] Save sources to storage"
  127. storage["sources"] = sources
  128. storage.sync()
  129. return plugin.finish(items, view_mode=get_view_mode(view_mode), update_listing=False, cache_to_disc=False)
  130. def get_list(data):
  131. if isinstance(data,unicode):
  132. data = data.encode("utf8")
  133. content = sources.get_content(data)
  134. print "[playstream] %s items returned"%len(content)
  135. items = []
  136. i = 1
  137. for item in content:
  138. if item[1] == "back": continue
  139. title = item[0].decode("utf8") if isinstance(item[0],str) else item[0]
  140. data2 = item[1].decode("utf8") if isinstance(item[1],str) else item[1]
  141. is_playable = True if sources.is_video(item[1]) else False
  142. img = item[2].decode("utf8") if isinstance(item[2],str) else item[2]
  143. desc = item[3].decode("utf8") if isinstance(item[3],str) else item[3]
  144. #print title.encode("utf8"),data2,img
  145. context_menu = [
  146. #("Add to PlayStream playlist",
  147. # u'RunScript(special://home/addons/%s/context_menu.py,"playlist","%s","%s","%s","%s")' % (
  148. # plugin.id, title, data2, playlist, proxy_url)),
  149. ("Add to PlayStream favorites",
  150. u'RunScript(special://home/addons/%s/context_menu.py,"add","%s","%s","%s","%s")'%(
  151. plugin.id, title, data2 ,img, desc)),
  152. ]
  153. if data.startswith("config::"):
  154. lst = data.split("::")[1]
  155. context_menu.extend([
  156. ("Delete from PlayStream favorites",
  157. u'RunScript(special://home/addons/%s/context_menu.py,"delete","%s","%s")' % (
  158. plugin.id, lst, i)),
  159. ("Move in PlayStream favorites",
  160. u'RunScript(special://home/addons/%s/context_menu.py,"move","%s","%s")' % (
  161. plugin.id, lst, i)),
  162. ])
  163. if True:
  164. context_menu.extend([
  165. ("Download",
  166. u'RunScript(special://home/addons/%s/context_download.py,"download","%s","%s","%s")' % (
  167. plugin.id, title, data2, download_dir)),
  168. ])
  169. item = {
  170. "label": title,
  171. "path": prefix+data2,
  172. "thumbnail":thumb_data(img, is_playable),
  173. #"poster":thumb_data(img, is_playable) ,
  174. "icon":thumb_data(img, is_playable) ,
  175. "info":{"plot":desc},
  176. "is_playable":is_playable,
  177. "context_menu": context_menu,
  178. }
  179. if view_mode == "Poster":
  180. item["poster"] = thumb_data(img, is_playable)
  181. items.append(item)
  182. i += 1
  183. return items
  184. def play_video(streams):
  185. if len(streams)>1:
  186. slist = []
  187. for s in streams:
  188. slist.append("%s [%s,%s]"%(s["name"],s["quality"],s["lang"]))
  189. res = xbmcgui.Dialog().select("Select stream",slist) if not CLI_MODE else 0
  190. #res = xbmcgui.Dialog().contextmenu(slist) if not CLI_MODE else 0
  191. stream = streams[res]
  192. else:
  193. stream = streams[0]
  194. subfiles = []
  195. #stream = util.stream_chamge(stream)
  196. if use_proxy:
  197. if "resolver" in stream and stream["resolver"] in ("hqq","filmas") or \
  198. "surl" in stream and re.search("http*://(hqq|goo\.gl)",stream["surl"]) or \
  199. "lattelecom.tv/mobile-vod/" in stream["url"]: # TODO
  200. #re.search(r"http*://.+?lattelecom\.tv/.+?auth_token=[^=]+=", stream["url"]):
  201. stream["url"] = util.streamproxy_encode(stream["url"],stream["headers"],proxy_url)
  202. stream["headers"] = {}
  203. if stream["headers"]:
  204. hh = []
  205. for k in stream["headers"]:
  206. h = "%s=%s"%(k,urllib.quote(stream["headers"][k]))
  207. hh.append(h)
  208. hh = "&".join(hh)
  209. stream["url"] = stream["url"] +"|"+hh
  210. print "[playstream] play_video ", stream["url"]
  211. if "subs" in stream and stream["subs"]:
  212. for sub in stream["subs"]:
  213. suburl = sub["url"]
  214. subs = util.Captions(suburl)
  215. srt = subs.get_srt()
  216. #subfile = plugin.temp_fn("subtitles.srt")
  217. subfile = os.path.join(os.path.dirname(__file__),sub["lang"]+".srt")
  218. f = open(subfile, "w")
  219. f.write(srt)
  220. f.close()
  221. subfiles.append(subfile)
  222. item = ListItem(label=stream["name"], thumbnail=thumb_data(stream["img"], True), path=stream["url"])
  223. item.set_info("video",{"plot":stream["desc"]})
  224. item.set_is_playable(True)
  225. return plugin.set_resolved_url(item,subfiles)
  226. #return plugin.play_video(item)
  227. def thumb_data(img, video=False):
  228. default = "video.png" if video else "folder.png"
  229. if img in ("default", ""):
  230. img = default
  231. if not img.startswith("http"):
  232. img = icong_url + img
  233. return img
  234. def get_view_mode(vm):
  235. modes = {
  236. "skin.estuary": {
  237. "None": None,
  238. "List": 50,
  239. "Poster": 51,
  240. "IconWall":52 ,
  241. "Shift": 53,
  242. "InfoWall": 54,
  243. "WideList": 55,
  244. "Wall": 500,
  245. "Banner": 501,
  246. "FanArt": 502
  247. }
  248. }
  249. skin = xbmc.getSkinDir()
  250. if skin in modes and vm in modes[skin]:
  251. view_mode = modes[skin][vm]
  252. else:
  253. view_mode = 50
  254. return view_mode
  255. if __name__ == '__main__':
  256. if CLI_MODE:
  257. from kodiswift.cli.cli import main as start
  258. start()
  259. else:
  260. plugin.run()
  261. if use_storage and storage is not None:
  262. print "[playstream] Save sources to storage"
  263. storage["sources"] = sources
  264. storage.sync()
  265. print "Save sources to storage"
  266. #pickle.dump(sources,open(storage_path,"wb"),pickle.HIGHEST_PROTOCOL)