Python module (submodule repositary), which provides content (video streams) from various online stream sources to corresponding Enigma2, Kodi, Plex plugins

replay.py 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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 os
  13. import urllib2, urllib
  14. import datetime, re, sys
  15. import ssl
  16. if "_create_unverified_context" in dir(ssl):
  17. ssl._create_default_https_context = ssl._create_unverified_context
  18. from SourceBase import SourceBase
  19. import util
  20. API_URL = 'https://replay.lsm.lv/%s/'
  21. headers2dict = lambda h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
  22. headers0 = headers2dict("""
  23. User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; Nexus 5 Build/KTU84P) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
  24. """)
  25. import HTMLParser
  26. h = HTMLParser.HTMLParser()
  27. from YouTubeVideoUrl import YouTubeVideoUrl
  28. class Source(SourceBase):
  29. def __init__(self,country="lv",cfg_path=None):
  30. self.name = "replay"
  31. self.title = "Replay.lv (LTV)"
  32. self.img = "replay.png"
  33. self.desc = "LSM replay.lv satura skatīšanās"
  34. self.country=country
  35. self.pic_size = "327x250" #"1000x765"
  36. def get_content(self, data):
  37. print "[replay] get_content:", data
  38. if "::" in data:
  39. data = data.split("::")[1]
  40. path = data.split("?")[0]
  41. clist = path.split("/")[0]
  42. params = data[data.find("?"):] if "?" in data else ""
  43. qs = dict(map(lambda x:x.split("="),re.findall("\w+=\w+",params)))
  44. lang = qs["lang"] if "lang" in qs else self.country
  45. content=[]
  46. content.append(("..return", "back","back.png","Return back"))
  47. if clist=="home":
  48. content.extend([
  49. ("Live streams", "replay::tiesraide","","TV live streams"),
  50. ("Search LV", "replay::search/?term={0}&lang=lv","","Search content LV"),
  51. ("Last videos LV", "replay::visi/jaunakie/?source=ltv&lang=lv","","Last aired videos LV"),
  52. ("Last videos by categories LV", "replay::kategorijas/?lang=lv","","Last videos by categories LV"),
  53. ("All programs LV", "replay::raidijumi/?type=video","","All programs by name LV"),
  54. ("Programs by categories LV", "replay::categories?lang=lv","","All programs by categories LV"),
  55. #("Channels", "replay::channels?language=%s"%self.country,"","TV channels"),
  56. ("Videos by popularity LV", "replay::visi/popularie/?source=ltv&lang=lv","","Programs by popularity"),
  57. ("Search RU", "replay::search/?term={0}&lang=ru","","Search content RU"),
  58. ("Last videos RU", "replay::vse/novie/?source=ltv&lang=ru","","Last aired videos RU"),
  59. ("Last videos by categories RU", "replay::kategorijas/?lang=ru","","Last videos by categories RU"),
  60. ("All programs RU", "replay::peredachi/?lang=ru&type=video","","All programs by name"),
  61. ("Programs by categories RU", "replay::categories?lang=ru","","Programs by categories RU")
  62. ])
  63. return content
  64. ### programmu kategorijas ###
  65. elif clist=="categories":
  66. url = "https://replay.lsm.lv/lv/raidijumi/?lang=lv&type=video" if lang =="lv" else "https://replay.lsm.lv/ru/peredachi/?lang=ru&type=video"
  67. r = self._http_request(url)
  68. icons = {
  69. #TODO
  70. "1": "https://www.iconfinder.com/icons/314161/download/png/256",
  71. }
  72. for item in re.findall(r'<a .+href="(\?lang=\w+&type=video&theme=\d+)">([^<]+)</a>\t', r):
  73. title = item[1]
  74. data2 = url.split("?")[0]+item[0]
  75. data2 = data2.replace(API_URL%lang,"")
  76. theme = item[0].split("theme=")[1]
  77. img = self.img # TODO icons["1"]
  78. desc = title
  79. content.append((title,self.name+"::"+data2,img,desc))
  80. return content
  81. ### jaunāko raidijumu kategorijas ###
  82. elif clist=="kategorijas":
  83. url = "https://replay.lsm.lv/lv/" if lang =="lv" else "https://replay.lsm.lv/ru/"
  84. r = self._http_request(url)
  85. for item in re.findall(r'<a href="/(lv|ru)/kategorija/(\w+)/">.+?<i class="[^"]+"></i>.+?<span>([^<]+)</span>', r, re.DOTALL):
  86. title = item[2]
  87. data2 = "kategorija/%s/?lang=%s"%(item[1],item[0])
  88. img = self.img
  89. desc = title
  90. content.append((title,self.name+"::"+data2,img,desc))
  91. return content
  92. ### Tiešraides kanānālu saraksts
  93. elif path=="tiesraide":
  94. url2 = "https://replay.lsm.lv/styles/main.css"
  95. r2= self._http_request(url2)
  96. url = "https://replay.lsm.lv/lv/tiesraide/ltv1/"
  97. # <div class="visible-xs">
  98. r= self._http_request(url)
  99. i2 = r.find('<div class="visible-xs">')
  100. result = re.findall(r"""onclick="RE.tools.navigate\('/lv/(tiesraide/\w+/)'\).+?<h5>([^<]+)</h5>.+?<time>([^<]+)</time>""", r[0:i2], re.DOTALL)
  101. for item in result:
  102. data2 = item[0]
  103. ch = data2.split("/")[1]
  104. title = ch.upper()
  105. m = re.search(r'channel-logo--%s{background-image:url\("([^"]+)"\)' % ch, r2, re.DOTALL)
  106. img = "https://replay.lsm.lv" + m.group(1)
  107. desc = item[1]
  108. # 00:10 &ndash; 00:45
  109. desc = desc + " (%s)" % item[2].replace("&ndash;", "-")
  110. title = title + " - " + desc
  111. content.append((title,self.name+"::"+data2,img,desc))
  112. return content
  113. ### Kanāla tiesraide
  114. elif clist == "tiesraide" and "/" in data:
  115. ch = data.split('/')[1]
  116. veids = "audio" if "lr" in ch else "video"
  117. #url = "https://replay.lsm.lv/lv/tiesraide/ltv7/"
  118. url = "https://replay.lsm.lv/lv/tiesraide/%s/"%ch
  119. r= self._http_request(url)
  120. m = re.search('%s/">.+?<h5>([^<]+)+</h5>.*?<time>([^<]+)</time>'%ch, r, re.DOTALL)
  121. tagad = m.group(1).strip() if m else ""
  122. laiks = m.group(2).strip() if m else ""
  123. laiks = h.unescape(laiks).encode("utf8")
  124. m = re.search("<h1>([^<]+)</h1>", r)
  125. title = m.group(1).strip() if m else path.split("/")[1].upper()
  126. title = "%s - %s (%s)"%(title,tagad,laiks)
  127. if veids == "video":
  128. m = re.search('<div class="video"><iframe.+src="([^"]+)"', r)
  129. if not m:
  130. raise Exception("No stream found")
  131. url = m.group(1)
  132. headers = headers2dict("""
  133. User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; Nexus 5 Build/KTU84P) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
  134. Referer: https://replay.lsm.lv/lv/ieraksts/ltv/70398/tiesa-runa.-lielbritanija-gatavojas-referendumam-par-tu/
  135. """)
  136. r = self._http_request(url,headers=headers)
  137. #m = re.search('<div class="video-player"><iframe.+src="([^"]+)"', r)
  138. m = re.search(r'iframe src=\\"([^"]+)\\', r)
  139. if not m:
  140. raise Exception("No stream found")
  141. url = m.group(1).replace('\\/', "/")
  142. m = re.search('poster":"([^"]+)"', r, re.DOTALL)
  143. img = "https://ltv.lsm.lv" + m.group(1).replace("\\","") if m else ""
  144. r = self._http_request(url,headers=headers)
  145. m = re.search('"([^"]+m3u8[^"]+)"', r)
  146. if not m:
  147. raise Exception("No stream found")
  148. data2 = m.group(1).replace("\\","")
  149. #r = self._http_request(data2, headers=headers)
  150. else: # audio
  151. lrn = ch.replace("lr","")
  152. url = "https:///www.latvijasradio.lsm.lv/lv/tiesraide/?channel=%s"%lrn
  153. r = self._http_request(url)
  154. m = re.search('"file":"([^"]+?m3u8.*?)"', r)
  155. if not m:
  156. raise Exception("No stream found")
  157. data2 = m.group(1).replace("\\","")
  158. img = self.img
  159. desc = ""
  160. desc = title
  161. content =(title,data2,img,desc)
  162. return content
  163. #m = re.search(r'(\?page=\d+)" class=" paging__prev', r, re.IGNORECASE)
  164. #if m:
  165. # data = re.sub("\?page=\d+", "", data)
  166. # data2 = data+m.group(1)
  167. # content.append(("Previous page",self.name+"::"+data2,"","Previous page"))
  168. r = self.call(data, lang=lang)
  169. if not r:
  170. return content
  171. if clist == "search":
  172. #for r2 in re.findall('<article itemtype="http://schema.org/Article" itemscope class="thumbnail thumbnail--default ">(.+?)</article>', r2, re.DOTALL):
  173. for item in re.findall('itemprop="image" data-image="([^"]+)".+?<figcaption><h5 itemprop="name"><a itemprop="url" href="([^<]+)">([^<]+)</a></h5></figcaption>', r):
  174. title = item[2]
  175. data2 = item[1].replace("/%s/"%lang,"")+"?lang=%s"%lang
  176. img = "https://replay.lsm.lv" + item[0]
  177. desc = title
  178. content.append((title,self.name+"::"+data2,img,desc))
  179. #for item in re.findall('itemprop="image" data-image="([^"]+)".+?<figcaption><h4 itemprop="about"><a href="([^"]+)">([^<]+)</a></h4>.*?<h5 itemprop="name"><a itemprop="url" href="([^"]+)">([^<]+)</a></h5>.+?datetime="([^"]+)" class="thumbnail__date ">([^<]+)</time>', r2):
  180. for item in re.findall('itemprop="image" data-image="([^"]+)".+? class="icon-(ltv|lr).+?<figcaption><h4 itemprop="about"><a href="([^"]+)">([^<]+)</a></h4>.*?<h5 itemprop="name"><a itemprop="url" href="([^"]+)">([^<]+)</a></h5>.+?datetime="([^"]+)" class="thumbnail__date ">([^<]+)</time>', r):
  181. if item[1]=="lr":continue
  182. title = "%s - %s (%s)"%(item[3],item[5],item[7])
  183. data2 = item[4].replace("/%s/"%lang,"")+"?lang=%s"%lang
  184. img = item[0] #.replace("https:","http:")
  185. img = img.replace("lv//", "lv/") # TODO eksperiments
  186. desc = title
  187. content.append((title,self.name+"::"+data2,img,desc))
  188. ### Raidijumi (programmas) ###
  189. elif clist in ( "raidijumi","peredachi"):
  190. for item in re.findall('<li itemprop="name"><a href="([^"]+)" itemprop="url">([^<]+)', r):
  191. #for item in re.findall('<li itemprop="name"><a href="([^"]+)" itemprop="url">([^<]+)</a></li>', r):
  192. title = item[1].strip()
  193. data2 = item[0].replace("/%s/"%lang,"")+"?lang=%s"%lang
  194. img = ""
  195. desc = ""
  196. content.append((title,self.name+"::"+data2,img,desc))
  197. ### Raidijuma ieraksti speciālie###
  198. elif clist in ( "visi","vse",):
  199. for item in re.findall('(?i)<figure><a href="([^"]+)" itemprop="image" data-image="([^"]+)".+class="thumbnail__duration">([^<]+)</time></figure><figcaption><h4 itemprop="about"><a href="[^"]+">([^<]+)</a></h4>.+>([^<]+).*</h5>.+>([^<]+)</time></figcaption>', r):
  200. title = item[3].strip()
  201. data2 = item[0].replace("/%s/"%lang,"")+"?lang=%s"%lang
  202. img = item[1] #.replace("https:","http:")
  203. img = img.replace("lv//", "lv/") # TODO eksperiments
  204. desc = "%s - %s\n%s"%(item[5],item[2],item[4])
  205. content.append((title,self.name+"::"+data2,img,desc))
  206. ### Raidijuma ieraksti (videos)
  207. elif clist in ("raidijums","peredacha","kategorija"):
  208. for item in re.findall('<article .+ href="([^"]+)".+image="([^"]+)".+class="thumbnail__duration">([^<]+).+">([^<]+).+class="thumbnail__date ">([^"]+)</time></figcaption></article>', r):
  209. title = item[3].strip()
  210. data2 = item[0].replace("/%s/"%lang,"")+"?lang=%s"%lang
  211. img = item[1] #.replace("https:","http:")
  212. img = img.replace("lv//", "lv/") # TODO eksperiments
  213. desc = "%s - %s"%(item[4],item[2])
  214. desc = desc.strip()
  215. content.append((title,self.name+"::"+data2,img,desc))
  216. ### Ieraksts (video) ###
  217. elif clist in ("ieraksts","statja"):
  218. m = re.search('src="([^"]+)"></iframe>', r)
  219. if m:
  220. url2 = m.group(1)
  221. m = re.search('<meta property="og:title" content="([^"]+)"', r, re.DOTALL)
  222. title = m.group(1)
  223. m = re.search('<meta property="og:image" content="([^"]+)"', r, re.DOTALL)
  224. img = m.group(1)
  225. img = img.replace("lv//", "lv/") # TODO eksperiments
  226. m = re.search('<meta property="og:description" content="([^"]+)"', r, re.DOTALL)
  227. desc = m.group(1)
  228. m = re.search('<div class="media-description">(.+)<div id="tab-comments">', r, re.DOTALL)
  229. if m:
  230. rr = re.sub("<.+?>", " ", m.group(1))
  231. rr = re.sub("[\t\n]", " ", rr)
  232. desc = re.sub(" {2,100}", " ", rr).strip()
  233. headers = headers2dict("""
  234. User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; Nexus 5 Build/KTU84P) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
  235. Referer: https://replay.lsm.lv/lv/ieraksts/ltv/70398/tiesa-runa.-lielbritanija-gatavojas-referendumam-par-tu/
  236. """)
  237. r2 = self._http_request(url2,headers=headers)
  238. m = re.search('"file":"([^"]+)', r2)
  239. if m:
  240. data2 = m.group(1).replace("\\","")
  241. m = re.search('"idstring":"([^"]+)', r2)
  242. #title = m.group(1).strip() if m else ""
  243. #title = title.decode("unicode-escape").encode("utf8")
  244. #title = title.replace("\n","")
  245. #img = ""
  246. #desc = ""
  247. if "youtube" in data2:
  248. video_id = re.search(r"/watch\?v=([^&]+)",data2).group(1)
  249. data2 = YouTubeVideoUrl().extract(video_id)
  250. if not data2:
  251. content=("No stream found %s"%data,"","","No stream found")
  252. return content
  253. content =(title,data2,img,desc)
  254. return content
  255. content=("No stream found %s"%data,"","","No stream found")
  256. return content
  257. m = re.search(r'href="\?([^"]+)" class=" paging__next', r)
  258. if m:
  259. page = int(re.search("page=(\d+)",m.group(1)).group(1))
  260. if "page="in data:
  261. data2 = re.sub("page=\d+","page=%i"%page,data)
  262. else:
  263. if "?" in data:
  264. data2 =data+"&page=%i"%page
  265. else:
  266. data2 =data+"?page=%i"%page
  267. content.append(("Next page",self.name+"::"+data2,"next.png","Next page"))
  268. return content
  269. def is_video(self,data):
  270. if "::" in data:
  271. data = data.split("::")[1]
  272. cmd = data.split("/")
  273. if cmd[0] in ("ieraksts","statja"):
  274. return True
  275. elif cmd[0]=="tiesraide" and len(cmd)>1:
  276. return True
  277. else:
  278. return False
  279. def call(self, data,headers=headers0,lang=""):
  280. if not lang: lang = self.country
  281. url = API_URL%lang + data
  282. #print "[TVPlay Api] url: ",url
  283. result = []
  284. content = self._http_request(url,headers=headers0)
  285. return content
  286. if __name__ == "__main__":
  287. sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
  288. import run
  289. source = Source()
  290. data= sys.argv[1] if len(sys.argv)>1 else source.name+"::home"
  291. run.run(source, data)
  292. sys.exit()