Enigma2 plugin to to play various online streams (mostly Latvian).

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. #!/usr/bin/env python
  2. # coding=utf8
  3. #
  4. # This file is part of PlayStream - enigma2 plugin to play video streams from various sources
  5. # Copyright (c) 2016 ivars777 (ivars777@gmail.com)
  6. # Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
  7. #
  8. try:
  9. import json
  10. except:
  11. import simplejson as json
  12. import urllib2, urllib
  13. import datetime, re, sys,os
  14. import ConfigParser
  15. from SourceBase import SourceBase
  16. import base64
  17. from collections import OrderedDict
  18. import sys
  19. try:
  20. import util
  21. except:
  22. sys.path.insert(0,'..')
  23. import util
  24. headers2dict = lambda h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
  25. class Source(SourceBase):
  26. def __init__(self,country=""):
  27. self.name = "filmix"
  28. self.title = "filmix.me"
  29. self.img = "http://cs5324.vk.me/g33668783/a_903fcc63.jpg"
  30. self.desc = "filmix.me satura skatīšanās"
  31. self.country=country
  32. self.headers = headers2dict("""
  33. Host: filmix.me
  34. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
  35. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  36. Accept-Language: en-US,en;q=0.5
  37. """)
  38. self.headers2 = headers2dict("""
  39. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
  40. X-Requested-With: XMLHttpRequest
  41. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  42. """)
  43. self.url = "https://filmix.me/"
  44. #self.login()
  45. def login(self,user="",password=""):
  46. return True
  47. def get_content(self, data):
  48. print "[filmix] get_content:", data
  49. source, data, path, plist, clist, params, qs = self.parse_data(data)
  50. content=[]
  51. content.append(("..return", "back","","Return back"))
  52. if clist=="home":
  53. content.extend([
  54. ("Search", "filmix::search/{0}","","Search"),
  55. ("Movies", "filmix::movies","","Movies"),
  56. ("Series", "filmix::series","","TV Series"),
  57. ("Cartoons", "filmix::cartoons","","Cartoons"),
  58. ])
  59. return content
  60. #elif clist=="search":
  61. # TODO
  62. #return content
  63. elif data in ("movies","series","cartoons"):
  64. r = self.call("")
  65. r = r.decode("cp1251").encode("utf8")
  66. if data == "movies":
  67. sname = "Фильмы"
  68. elif data=="series":
  69. sname = "Сериалы"
  70. else:
  71. sname = "Мультфильмы"
  72. # <span class="menu-title">Фильмы</span>
  73. m = re.search('<span class="menu-title">%s</span>(.+?)<li>\s+?<span'%sname, r, re.DOTALL|re.UNICODE)
  74. if not m: return content
  75. r2 = m.group(1)
  76. result = re.findall(r'<a .*?href="https://filmix\.me/([^"]+)".*?>([^<]+)</', r2, re.DOTALL)
  77. for item in result:
  78. if "catalog" in item[0]: continue
  79. title = item[1]
  80. data2 = item[0]
  81. img = self.img
  82. desc = title
  83. content.append((title,self.name+"::"+data2,img,desc))
  84. return content
  85. ## Seriāls
  86. elif clist=="play":
  87. r = self.call(path)
  88. r = r.decode("cp1251").encode("utf8")
  89. title = title0 = util.unescape(re.search("titlePlayer = '([^']+)'", r, re.DOTALL).group(1))
  90. m = re.search('<meta itemprop="thumbnailUrl" content="([^"]+)',r,re.DOTALL)
  91. img = m.group(1) if m else self.img
  92. m = re.search('<meta itemprop="duration" content="([^"]+)" />', r, re.DOTALL)
  93. duration = "(%s)"%m.group(1) if m else ""
  94. m = re.search('<p itemprop="description"[^>]+>([^<]+)<', r, re.DOTALL)
  95. desc = desc0 = util.unescape(m.group(1).strip()) if m else ""
  96. vid = plist[-1]
  97. js = self.get_movie_info(vid)
  98. pl_link = js["message"]["translations"]["flash"].values()[0] # TODO process several players
  99. pl_link = self.decode_uppod_text(pl_link)
  100. js = self._http_request(pl_link)
  101. js = self.decode_uppod_text(js)
  102. js = json.loads(js)
  103. if "s" in qs:
  104. s = int(qs["s"])
  105. for i,ep in enumerate(js["playlist"][s-1]["playlist"]):
  106. title = title0+" - "+js["playlist"][s-1]["playlist"][i]["comment"].encode("utf8")
  107. serie = js["playlist"][s-1]["playlist"][i]["comment"].encode("utf8")
  108. data2 = data+"&e=%s"%(i+1)
  109. desc = serie +"\n"+desc0
  110. content.append((title,self.name+"::"+data2,img,desc))
  111. else:
  112. for i,ep in enumerate(js["playlist"]):
  113. title = title0 +" - "+js["playlist"][i]["comment"].encode("utf8")
  114. serie = js["playlist"][i]["comment"].encode("utf8")
  115. data2 = data+"?s=%s"%(i+1)
  116. desc = serie +"\n"+desc0
  117. content.append((title,self.name+"::"+data2,img,desc))
  118. return content
  119. #r = self._http_request(url)
  120. ### saraksts ###
  121. else:
  122. r = self.call(data)
  123. r = r.decode("cp1251").encode("utf8")
  124. for r2 in re.findall('<article class="shortstory line".+?</article>', r, re.DOTALL):
  125. m = re.search(r'<a href="https://filmix\.me/play/(\d+)" class="watch icon-play">', r2, re.DOTALL)
  126. if not m: continue
  127. vid = m.group(1)
  128. data2 = "play/%s"%vid
  129. #title = re.search('itemprop="name">([^<]+)</div>', r2, re.DOTALL).group(1)
  130. title = re.search('itemprop="name" content="([^"]+)"', r2, re.DOTALL).group(1)
  131. m = re.search('itemprop="alternativeHeadline" content="([^"]+)"', r2, re.DOTALL)
  132. if m:
  133. title = title + "/"+m.group(1)
  134. m = re.search('<img src="([^"]+.jpg)"', r2, re.DOTALL)
  135. img = "http://filmix.me"+m.group(1) if m else self.img
  136. m = re.search(r'<a itemprop="copyrightYear".+?>(\d+)<', r2, re.DOTALL)
  137. if m:
  138. year = m.group(1) if m else ""
  139. title = "%s (%s)"%(title,year)
  140. title = util.unescape(title)
  141. genre = re.findall('<a itemprop="genre"[^>]+?">([^<]+)</a>', r2, re.DOTALL)
  142. genre = ",".join(genre)
  143. m = re.search('<p itemprop="description">([^<]+)</p>', r2, re.DOTALL)
  144. desc0 = util.unescape(m.group(1)) if m else ""
  145. m = re.search('<div class="quality">([^<]+)</div>', r2, re.DOTALL)
  146. quality = m.group(1) if m else ""
  147. actors = re.findall('itemprop="actor">([^<]+)<', r2, re.DOTALL)
  148. actors = ",".join(actors)
  149. desc="%s\n%s\n%s\n%s\n%s"%(title,genre,desc0,actors,quality)
  150. content.append((title,self.name+"::"+data2,img,desc))
  151. if '<div class="navigation">' in r:
  152. m = re.search(r'href="https://filmix\.me/([^"]+)" class="next icon-arowRight btn-tooltip"', r, re.DOTALL)
  153. if m:
  154. data2 = m.group(1)
  155. else:
  156. m = re.search("/page/(\d)+",data)
  157. if m:
  158. page = int(m.group(1))+1
  159. data2 = re.sub("/page/(\d)+", "/page/%s"%page, data)
  160. else:
  161. data2 = data + "/page/2"
  162. content.append(("Next page",self.name+"::"+data2,self.img,"Next page"))
  163. return content
  164. def is_video(self,data):
  165. source,data,path,plist,clist,params,qs = self.parse_data(data)
  166. if clist == "play" and "s=" in data and "e=" in data:
  167. return True
  168. elif clist=="play" and not params:
  169. r = self.call(path)
  170. #r = r.decode("cp1251").encode("utf8")
  171. m = re.search('itemprop="contentUrl" content="(.+?)"', r, re.IGNORECASE | re.DOTALL)
  172. if not m:
  173. raise Exception("Can not find video link")
  174. #return False
  175. video_link = m.group(1)
  176. if video_link=='{video-link}':
  177. return False
  178. else:
  179. return True
  180. else:
  181. return False
  182. def get_streams(self, data):
  183. print "[filmix] get_streams:", data
  184. source,data,path,plist,clist,params,qs = self.parse_data(data)
  185. r = self.call(path)
  186. if not r:
  187. return []
  188. streams = []
  189. r = r.decode("cp1251").encode("utf8")
  190. title = title0 = util.unescape(re.search("titlePlayer = '([^']+)'", r, re.DOTALL).group(1))
  191. m = re.search('<meta itemprop="thumbnailUrl" content="([^"]+)',r,re.DOTALL)
  192. img = m.group(1) if m else self.img
  193. m = re.search('<meta itemprop="duration" content="([^"]+)" />', r, re.DOTALL)
  194. duration = "(%s)"%m.group(1) if m else ""
  195. m = re.search('<p itemprop="description"[^>]+>([^<]+)<', r, re.DOTALL)
  196. desc = desc0 = util.unescape(m.group(1).strip()) if m else ""
  197. m = re.search('itemprop="contentUrl" content="(.+?)"', r, re.IGNORECASE | re.DOTALL)
  198. if not m:
  199. # #raise Exception("Can not find video link")
  200. return []
  201. video_link = m.group(1)
  202. series = True if video_link == '{video-link}' else False
  203. vid = plist[1]
  204. js = self.get_movie_info(vid)
  205. if js["message"]["translations"]["flash"]:
  206. video_link = js["message"]["translations"]["flash"].values()[0].encode("utf8")
  207. video_link = self.decode_uppod_text(video_link)
  208. lang = js["message"]["translations"]["flash"].keys()[0].encode("utf8") # TODO process several players/streams
  209. else:
  210. return []
  211. if not series : # Filma
  212. url0 = video_link
  213. streams2 = self.get_streams2(url0)
  214. for st in streams2:
  215. stream = util.item()
  216. stream["url"]=st[1]
  217. stream["lang"]=lang
  218. stream["quality"]=st[0]
  219. stream["name"]= title
  220. stream["desc"]=desc
  221. streams.append(stream)
  222. return streams
  223. else: # Seriāls
  224. pl_link = video_link
  225. js = self._http_request(pl_link)
  226. js = self.decode_uppod_text(js)
  227. js = json.loads(js)
  228. if "s" in qs and "e" in qs:
  229. s = int(qs["s"])
  230. e = int(qs["e"])
  231. serie = js["playlist"][s-1]["playlist"][e-1]["comment"].encode("utf8")
  232. title = title0+" - "+ serie
  233. url0 = js["playlist"][s-1]["playlist"][e-1]["file"].encode("utf8")
  234. streams2 = self.get_streams2(url0)
  235. for st in streams2:
  236. stream = util.item()
  237. stream["url"]=st[1]
  238. stream["lang"]=lang
  239. stream["quality"]=st[0]
  240. stream["name"]= title
  241. stream["desc"]=desc
  242. streams.append(stream)
  243. return streams
  244. def call(self, data,params=None,headers=None,lang=""):
  245. if not headers: headers = self.headers
  246. url = self.url+data
  247. result = self._http_request(url,params,headers=headers)
  248. return result
  249. def get_movie_info(self,vid):
  250. headers = headers2dict("""
  251. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0
  252. Accept: application/json, text/javascript, */*; q=0.01
  253. Accept-Language: en-US,en;q=0.5
  254. Content-Type: application/x-www-form-urlencoded; charset=UTF-8
  255. X-Requested-With: XMLHttpRequest
  256. Referer: https://filmix.me/play/%s
  257. """%vid )
  258. post_data = {"post_id":vid}
  259. r = util.post("https://filmix.me/api/movies/player_data", data=post_data, headers = headers)
  260. if not r:
  261. raise Exception("Can not get movie info")
  262. #return []
  263. js = json.loads(r)
  264. return js
  265. def decode_uppod_text(self, text):
  266. Client_codec_a = ["l", "u", "T", "D", "Q", "H", "0", "3", "G", "1", "f", "M", "p", "U", "a", "I", "6", "k", "d", "s", "b", "W", "5", "e", "y", "="]
  267. Client_codec_b = ["w", "g", "i", "Z", "c", "R", "z", "v", "x", "n", "N", "2", "8", "J", "X", "t", "9", "V", "7", "4", "B", "m", "Y", "o", "L", "h"]
  268. text = text.replace("\n", "").strip()
  269. for i in range(len(Client_codec_a)):
  270. char1 = Client_codec_b[i]
  271. char2 = Client_codec_a[i]
  272. text = text.replace(char1, "___")
  273. text = text.replace(char2, char1)
  274. text = text.replace("___", char2)
  275. result = base64.b64decode(text)
  276. print result
  277. return result
  278. def get_streams2(self,url0):
  279. m = re.search("\[([\d,]+)]",url0)
  280. if not m:
  281. return [("?",url0)]
  282. res = m.group(1)
  283. streams=[]
  284. for res in res.split(","):
  285. if not res: continue
  286. url=re.sub("\[[\d,]+]",res,url0)
  287. streams.append((res,url))
  288. return streams
  289. if __name__ == "__main__":
  290. c = Source()
  291. #s = "ZnVuY3Rpb24gc2VuZE1lc3NhZ2U2MDc3ODkoZSl7dmFyIGg9bWdfd3M2MDc3ODkub25tZXNzYWdlOyBtZ193czYwNzc4OS5yZWFkeVN0YXRlPT1tZ193czYwNzc4OS5DTE9TRUQmJihtZ193czYwNzc4OT1uZXcgV2ViU29ja2V0KG1nX3dzNjA3Nzg5X2xvY2F0aW9uKSksbWdfd3M2MDc3ODkub25tZXNzYWdlPWgsd2FpdEZvclNvY2tldENvbm5lY3Rpb242MDc3ODkobWdfd3M2MDc3ODksZnVuY3Rpb24oKXttZ193czYwNzc4OS5zZW5kKGUpfSl9ZnVuY3Rpb24gd2FpdEZvclNvY2tldENvbm5lY3Rpb242MDc3ODkoZSx0KXtzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7cmV0dXJuIDE9PT1lLnJlYWR5U3RhdGU/dm9pZChudWxsIT10JiZ0KCkpOnZvaWQgd2FpdEZvclNvY2tldENvbm5lY3Rpb242MDc3ODkoZSx0KX0sNSl9OyB2YXIgbWdfd3M2MDc3ODlfbG9jYXRpb24gPSAid3NzOi8vd3NwLm1hcmtldGdpZC5jb20vd3MiOyBtZ193czYwNzc4OSA9IG5ldyBXZWJTb2NrZXQobWdfd3M2MDc3ODlfbG9jYXRpb24pLCBtZ193czYwNzc4OS5vbm1lc3NhZ2UgPSBmdW5jdGlvbiAodCkge3Bvc3RNZXNzYWdlKHQuZGF0YSk7fSwgb25tZXNzYWdlID0gZnVuY3Rpb24oZSl7c2VuZE1lc3NhZ2U2MDc3ODkoZS5kYXRhKX0="
  292. #txt = c.decode_uppod_text(s)
  293. if len(sys.argv)>1:
  294. data= sys.argv[1]
  295. else:
  296. data = "home"
  297. content = c.get_content(data)
  298. for item in content:
  299. print item
  300. #cat = api.get_categories(country)
  301. #chan = api.get_channels("lv")
  302. #prog = api.get_programs(channel=6400)
  303. #prog = api.get_programs(category=55)
  304. #seas = api.get_seasons(program=6453)
  305. #str = api.get_streams(660243)
  306. #res = api.get_videos(802)
  307. #formats = api.getAllFormats()
  308. #det = api.detailed("1516")
  309. #vid = api.getVideos("13170")
  310. pass