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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  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. import sys, os, os.path, re, sys
  9. import urllib,urllib2,urlparse
  10. #from xml.sax.saxutils import unescape,escape
  11. from urllib import quote, unquote
  12. import HTMLParser
  13. import json
  14. from SourceBase import SourceBase, stream_type
  15. import util
  16. from util import unescape
  17. from collections import OrderedDict
  18. import ssl
  19. if "_create_unverified_context" in dir(ssl):
  20. ssl._create_default_https_context = ssl._create_unverified_context
  21. import time, datetime, pytz
  22. from datetime import datetime as dt
  23. dt2ts = lambda dt: int(time.mktime(dt.timetuple()))
  24. ts2dt = lambda ts: datetime.datetime.fromtimestamp(ts)
  25. user_agent = "User-Agent: Shortcut.lv for Android TV v1.11.5 / Dalvik/2.1.0 (Linux; U; Android 6.0; Android SDK built for x86 Build/MASTER)"
  26. headers2dict = lambda h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
  27. h = HTMLParser.HTMLParser()
  28. class Source(SourceBase):
  29. def __init__(self,cfg_path=None):
  30. self.name = "ltc2"
  31. self.title = "Shortcut.lv"
  32. self.img = "shortcut.png"
  33. self.desc = "Shortcut.lv (lattelecom.tv) satura skatīšanās"
  34. self.token = ""
  35. self.session_id = "" # www.shortcut.lv
  36. self.api_url = "https://manstv.lattelecom.tv/api/v1.7/get/content/"
  37. self.headers = headers2dict("""
  38. User-Agent: Shortcut.lv for Android TV v1.11.5 / Dalvik/2.1.0 (Linux; U; Android 6.0; Android SDK built for x86 Build/MASTER)
  39. Connection: Keep-Alive
  40. """)
  41. self.headers2 = headers2dict("""
  42. Connection: keep-alive
  43. User-Agent: Shortcut.lv for Android TV v1.11.5 / Dalvik/2.1.0 (Linux; U; Android 6.0; Android SDK built for x86 Build/MASTER)
  44. Accept-Language: en-US,en;q=0.8
  45. """)
  46. self.channels = None
  47. ### specific service info ###
  48. self.ch = []
  49. self.ch2 = []
  50. self.ch_id={}
  51. self.ch_id2={}
  52. self.ch_name={}
  53. self.today = datetime.date.today()
  54. self.today2 = self.today.strftime("%d.%m.%Y")
  55. cur_directory = os.path.dirname(os.path.abspath(__file__))
  56. if not cfg_path: cfg_path = cur_directory
  57. self.config_file = os.path.join(cfg_path,self.name+".cfg")
  58. self.options = OrderedDict([("user","lietotajs"),("password","parole")])
  59. self.options_read()
  60. def get_content(self, data):
  61. print "[ltc] get_content:", data
  62. source, data, path, plist, clist, params, qs = self.parse_data(data)
  63. content=[]
  64. content.append(("..return", "back","back.png","Return back"))
  65. if clist=="home":
  66. content.extend([
  67. ("TV tiešraides", "ltc::tiesraide","","TV tiešraides"),
  68. ("TV arhīvs", "ltc::pages/archive","","TV arhīvs atseviškiem kanāliem"),
  69. ("Bērnu", "ltc::pages/kids","","Videonomas bērnu filmas un seriāli"),
  70. ("Filmas", "ltc::pages/films","","Videonomas filmas"),
  71. ("Premiere filmas", "ltc::pages/premiere","","Videonomas premiere filmas"),
  72. ("Seriāli", "ltc::pages/series","","Videonomas seriāli"),
  73. ("360 Play", "ltc::/pages/360-play","","Videonomas lattelecom saturs"),
  74. ("Meklēt", "ltc::search/{0}","","Meklēt visā saturā"), #TODO
  75. ])
  76. return content
  77. ### Meklēt TV (1.7 API)
  78. elif clist == "search": #TODO vod/search/{q}
  79. q = plist[1]
  80. r = self.call("pages/search/?filter[term]=%s&include=items,items.item" % q)
  81. # TODO
  82. return content
  83. ### Tiešraides ####
  84. elif data=="tiesraide":
  85. now = dt2ts(dt.now())
  86. r = self.call("epgs/?filter[utFrom]=%s&filter[utTo]=%s&include=channel&page[size]=1000" % (now, now))
  87. epgs = r["data"]
  88. for item in self.get_channels():
  89. title = item["attributes"]["title"]
  90. data2 = "live-streams/%s?include=quality"%item["id"]
  91. for epg in epgs:
  92. if item["id"] == epg["relationships"]["channel"]["data"]["id"]:
  93. epg = epg["attributes"]
  94. break
  95. else:
  96. epg = None
  97. if epg:
  98. start = ts2dt(int(epg["unix-start"]))
  99. start = start.strftime("%H:%M")
  100. end = ts2dt(int(epg["unix-stop"]))
  101. end = end.strftime("%H:%M")
  102. title = "%s - %s [%s-%s]"%(title,epg["title"],start,end)
  103. desc = title
  104. if epg["description"]:
  105. desc += "\n" + epg["description"]
  106. img = "https://manstv.lattelecom.tv/"+ epg["poster-url"]
  107. else:
  108. img = "https://manstv.lattelecom.tv/"+ item["attributes"]["logo-url"]
  109. desc = item["attributes"]["description"]
  110. content.append((title,self.name+"::"+data2,img,desc))
  111. return content
  112. ### Pages ###
  113. elif clist=="pages" or data=="arhivs":
  114. r = self.call(data+"?page[size]=1000")
  115. for item in r["data"]:
  116. title = item["attributes"]["title"]
  117. data2 = item["links"]["self"][1:] + "?include=items"
  118. img = self.img
  119. desc = title
  120. content.append((title,self.name+"::"+data2,img,desc))
  121. return content
  122. ### Kategorija ###
  123. elif clist=="categories" and len(plist) > 1:
  124. r = self.call(data)
  125. for item in r["items"]:
  126. title = item["name"]
  127. data2 = "archive/records?filter[category]=%s&limit=40"%item["id"]
  128. img = "https://manstv.lattelecom.tv/"+ item['image']
  129. desc = title
  130. content.append((title,self.name+"::"+data2,img,desc))
  131. return content
  132. ### Arhīva kategorijas
  133. elif data=="archive/categories":
  134. #https://manstv.lattelecom.tv/api/v1.3/get/archive/records/?filter[category]=13&limit=10&until_id=1458681019238
  135. #https://manstv.lattelecom.tv/api/v1.3/get/archive/categories/
  136. r = self.call(data)
  137. for item in r["items"]:
  138. title = item["name"]
  139. data2 = "archive/records?filter[category]=%s&limit=40"%item["id"]
  140. img = "https://manstv.lattelecom.tv/"+ item['image']
  141. desc = title
  142. content.append((title,self.name+"::"+data2,img,desc))
  143. return content
  144. ### Arhīva kategoriju video
  145. elif "archive/records" in data:
  146. #https://manstv.lattelecom.tv/api/v1.3/get/archive/records/?filter[category]=13&limit=10&until_id=1458681019238
  147. #https://manstv.lattelecom.tv/api/v1.3/get/archive/categories/
  148. r = self.call(data)
  149. for i,item in enumerate(r["items"]):
  150. #if not item["is_archive"]==u"1":continue # TODO jānočeko, kurs no atributiem apzīmē, ka ir arhīvs
  151. #if not self.epg_id.has_key(item["id"]):
  152. item["time_start2"] = datetime.datetime.fromtimestamp(int(item["unix_start"]))
  153. item["time_stop2"] = datetime.datetime.fromtimestamp(int(item["unix_stop"]))
  154. item["date"]=item["time_start2"].strftime("%Y-%m-%d")
  155. #self.epg.append(item)
  156. #index = self.epg.index(item)
  157. #self.epg_id[item["id"]]=index
  158. #if not item["date"] in self.epg_date:
  159. # self.epg_date[item["date"]]=[]
  160. #self.epg_date[item["date"]].append(index)
  161. #if not item["channel_id"] in self.epg_ch:
  162. # self.epg_ch[item["channel_id"]]=[]
  163. #self.epg_ch[item["channel_id"]].append(index)
  164. #item = self.get_epg_id(item["id"])
  165. ch = self.get_channel_by_id2(item["channel_id"])
  166. ch_name = ch["name"] if ch else item["channel_id"]
  167. title = u"%s (%s-%s)"%(item["title"], item["time_start2"].strftime("%H:%M"),item["time_stop2"].strftime("%H:%M"))
  168. #data2 = "archive/get-stream/%s?channelid=%s"%(item["id"],ch["id"])
  169. data2 = "content/record-streams/%s?include=quality"%item["id"]
  170. img = "https://manstv.lattelecom.tv/"+ item["url"]
  171. desc = u"%s - %s\n%s %s-%s\n%s\n%s"%(ch_name,item["title"], item["date"], item["time_start2"].strftime("%H:%M"),item["time_stop2"].strftime("%H:%M"),
  172. item["category1"],item["description"])
  173. content.append((title,self.name+"::"+data2,img,desc))
  174. if "until_id" in data:
  175. data2 = re.sub("until_id=\d+","until_id="+item["id"],data)
  176. else:
  177. data2 = data + "&until_id=%s"%item["id"]
  178. content.append(("Next page",self.name+"::"+data2,"next.png","Go to next page"))
  179. return content
  180. ### Arhīva kanānālu saraksts
  181. elif data=="archive/channels":
  182. for item in self.get_channels():
  183. if not item["is_archive"]==u"1":continue
  184. title = item["name"]
  185. data2 = "archive/channel/%s?date=%s"%(item["id"],self.today.strftime("%Y-%m-%d"))
  186. img = "https://manstv.lattelecom.tv/"+ item['broadcast_default_picture']
  187. desc = title
  188. content.append((title,self.name+"::"+data2,img,desc))
  189. return content
  190. ### Arhīva kanānāla video saraksta
  191. elif "archive/channel/" in data:
  192. chid = path.split("/")[2]
  193. for item in self.get_epg_date(qs["date"],chid):
  194. if item["is_archive"]=='0':continue
  195. if item["time_stop2"] > datetime.datetime.now(): continue
  196. ch = self.get_channel_by_id2(item["channel_id"])
  197. ch_name = ch["name"] if ch else item["channel_id"]
  198. #title = u"[%s %s-%s] %s - %s"%(item["date"],item["time_start2"].strftime("%H:%M"),item["time_stop2"].strftime("%H:%M"),ch_name,item["title"])
  199. title = u"%s (%s-%s)"%(item["title"], item["time_start2"].strftime("%H:%M"),item["time_stop2"].strftime("%H:%M"))
  200. #data2 = "archive/get-stream/%s?channelid=%s"%(item["id"],ch["id"])
  201. data2 = "content/record-streams/%s?include=quality"%item["id"]
  202. img = "https://manstv.lattelecom.tv/"+ item["url"]
  203. desc = u"%s - %s\n%s %s-%s\n%s\n%s"%(ch_name,item["title"], item["date"], item["time_start2"].strftime("%H:%M"),item["time_stop2"].strftime("%H:%M"),
  204. item["category1"],item["description"])
  205. content.append((title,self.name+"::"+data2,img,desc))
  206. #date2=datetime.datetime.strptime(qs["date"], '%Y-%m-%d').date()-datetime.timedelta(days=1)
  207. date2 = datetime.date(*tuple(map(int, qs["date"].split("-")))) - datetime.timedelta(days=1)
  208. date2 = date2.strftime("%Y-%m-%d")
  209. if "date=" in data:
  210. data2 = re.sub("date=[\d-]+","date="+date2,data)
  211. else:
  212. data2 = data + "&date=%s"%date2
  213. content.append(("Previous day (%s)"%date2,self.name+"::"+data2,"","Go previous day"))
  214. return content
  215. ### Arhīva datumi
  216. elif clist=="arhivs" and len(data.split("/"))==2: # TODO - pasreiz nestrada
  217. ch = data.split("/")[1]
  218. r= self.call(data)
  219. m = re.search('class="spac no_select">([^<]+)</span>', r, re.IGNORECASE)
  220. if m:
  221. ch_name = m.group(1)
  222. else:
  223. ch_name = ""
  224. today = datetime.date.today()
  225. for i in range(7):
  226. date = today-datetime.timedelta(i)
  227. title = ch_name + " - " + date.strftime("%d.%m.%Y")
  228. data2 = "%s/%s"%(data,date.strftime("%Y-%m-%d"))
  229. img = ""
  230. desc = title
  231. content.append((title,self.name+"::"+data2,img,desc))
  232. return content
  233. ### Arhīva dienas raidijumi
  234. elif clist=="arhivs" and len(data.split("/"))==3: # TODO - nestrādā
  235. ch = data.split("/")[1]
  236. date = data.split("/")[2]
  237. r= self.call(data)
  238. m = re.search('class="spac no_select">([^<]+)</span>', r, re.IGNORECASE)
  239. if m:
  240. ch_name = m.group(1)
  241. else:
  242. ch_name = ""
  243. for item in re.findall('(?i)href="/([^"]+?)" id="" class="archive_programm_a"><div class="archive_programm "><div class="chanel_time"><span>([^<]+)</span></div><div class="archive_programm_one"><span>([^<]+)', r):
  244. title = "%s - %s"%(item[1],item[2])
  245. data2 = item[0]
  246. img = ""
  247. desc = "%s (%s)\n%s"%(ch_name,date,title)
  248. content.append((title,self.name+"::"+data2,img,desc))
  249. return content
  250. ### Videonoma galvenā (vecā, vairs nestrada filtri)
  251. elif data=="videonoma":
  252. content.extend([
  253. ("Filmas - jaunākās", "ltc::videonoma?page=0&genre=all_movies&sorts=laiks&cnt=40&clear=true&filter={}","https://www.shortcut.lv/media/features/2013-12-17/aa3b_videonoma_dropdown_filmas.png","Jaunākās filmas"),
  254. ("Filmas - pēc nosaukuma", "ltc::videonoma?page=0&genre=all_movies&sorts=title&cnt=40&clear=true&filter={}","https://www.shortcut.lv/media/features/2013-12-17/aa3b_videonoma_dropdown_filmas.png","Filmas pēc nosaukuma"),
  255. ("Filmas - latviski", 'ltc::videonoma?page=0&genre=all_movies&sorts=laiks&cnt=40&clear=true&filter={"valoda":["lv"]}',"https://www.shortcut.lv/media/features/2013-12-17/aa3b_videonoma_dropdown_filmas.png","Filmas latviešu valodā"),
  256. ("Filmas - pēc reitinga", "ltc::videonoma?page=0&genre=all_movies&sorts=imbd&cnt=40&clear=true&filter={}","https://www.shortcut.lv/media/features/2013-12-17/aa3b_videonoma_dropdown_filmas.png","Filmas pēc IMBD reitinga"),
  257. ("Filmas bērniem - jaunākās", "ltc::videonoma?page=0&genre=3&sorts=laiks&cnt=40&clear=true&filter={}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_berniem.png","Jaunākie bernu video"),
  258. ("Filmas bērniem - pēc nosaukuma", "ltc::videonoma?page=0&genre=3&sorts=title&cnt=40&clear=true&filter={}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_berniem.png","Bērnu video pēc nosaukuma"),
  259. ("Filmas bērniem - latviski", 'ltc::videonoma?page=0&genre=3&sorts=laiks&cnt=40&clear=true&filter={"valoda":[\"lv\"]}',"https://www.shortcut.lv/images/redesign/videonoma_dropdown_berniem.png","Bērnu video latviski"),
  260. ("Filmas bērniem - pēc reitinga", "ltc::videonoma?page=0&genre=3&sorts=imbd&cnt=40&clear=true&filter={}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_berniem.png","Bērnu video pēc IMBD reitinga"),
  261. ("Sērijas - jaunākās", "ltc::videonoma?page=0&genre=27&sorts=laiks&cnt=40&clear=true&filter={}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_raidijumi.png","Jaunākās sērijas"),
  262. ("Sērijas - pēc nosaukuma", "ltc::videonoma?page=0&genre=27&sorts=title&cnt=40&clear=true&filter={}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_raidijumi.png","Sērijas pēc pēc nosaukuma"),
  263. ("Sērijas animācijas - jaunākās", "ltc::videonoma?page=0&genre=27&sorts=laiks&cnt=40&clear=true&filter={\"zanrs\":[\"201\"]}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_raidijumi.png","Animācijas serijas"),
  264. ("Sērijas animācijas - pēc nosaukuma", "ltc::videonoma?page=0&genre=27&sorts=title&cnt=40&clear=true&filter={\"zanrs\":[\"201\"]}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_raidijumi.png","Animācijas serijas"),
  265. ("Sērijas ģimenes - jaunākās", "ltc::videonoma?page=0&genre=27&sorts=laiks&cnt=40&clear=true&filter={\"zanrs\":[\"195\"]}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_raidijumi.png","Animācijas serijas"),
  266. ("Sērijas ģimenes - pēc nosaukuma", "ltc::videonoma?page=0&genre=27&sorts=title&cnt=40&clear=true&filter={\"zanrs\":[\"195\"]}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_raidijumi.png","Animācijas serijas"),
  267. ("Sērijas latviski - jaunākās", 'ltc::videonoma?page=0&genre=27&sorts=laiks&cnt=40&clear=true&filter={"valoda":["lv"]}',"https://www.shortcut.lv/images/redesign/videonoma_dropdown_raidijumi.png","Sērijas latviski"),
  268. ("Sērijas latviski - pēc nosaukuma", 'ltc::videonoma?page=0&genre=27&sorts=title&cnt=40&clear=true&filter={"valoda":["lv"]}',"https://www.shortcut.lv/images/redesign/videonoma_dropdown_raidijumi.png","Sērijas latviski"),
  269. ("Koncerti - jaunākie", "ltc::videonoma?page=0&genre=19&sorts=laiks&cnt=40&clear=true&filter={}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_koncerti.png","Jaunākie koncerti"),
  270. ("Koncerti - pēc nosaukuma", "ltc::videonoma?page=0&genre=19&sorts=title&cnt=40&clear=true&filter={}","https://www.shortcut.lv/images/redesign/videonoma_dropdown_koncerti.png","Koncerti pēc pēc nosaukuma"),
  271. ])
  272. return content
  273. ### Videonomas saraksti (vecie, vairs nestrādā filtri)
  274. elif path == "videonoma":
  275. url = "https://www.shortcut.lv/movies-snippet.json"+params
  276. r = self._http_request(url,headers=self.headers2)
  277. if not r:
  278. return content
  279. js = json.loads(r,"utf8")
  280. if not r:
  281. return content
  282. for item in js["movies"]:
  283. if not item["title"]:
  284. continue
  285. title = item["title"].encode("utf8")
  286. data2 = item["url"][1:].encode("utf8")
  287. if data2[-1]=="/": data2=data2[:-1]
  288. if "/raidijumi/" in data2:
  289. data2 += "?series" # TODO
  290. img = "https://www.shortcut.lv"+item["image"].encode("utf8")
  291. desc = "%s\n%s"%(title,item["genre"].encode("utf8"))
  292. if "is_premium" in item and item["is_premium"]:
  293. title = title + " [P]"
  294. desc = desc + "\nPremium"
  295. content.append((title,self.name+"::"+data2,img,desc))
  296. m = re.search("page=(\d+)",data)
  297. if m:
  298. page = int(m.group(1))
  299. data2 = re.sub("page=\d+","page=%s"%(page+1),data)
  300. content.append(("Next page",self.name+"::"+data2,"next.png","Go to next page"))
  301. return content
  302. ### Filmas galvenā ###
  303. elif data == "videonoma/filmas":
  304. content.extend([
  305. ("Aktuālās", "ltc::viss/actual_films","","Nesen pievienotas filmas"),
  306. ("Komēdijas", "ltc::viss/comedy","","Komēdijas"),
  307. ("Detektīvi", "ltc::viss/crime","","Detektivi"),
  308. ("Ģimenes", "ltc::viss/family","","Ģimenes filmas"),
  309. ("Asa sižeta", "ltc::viss/action","","Asa sižeta filmas"),
  310. ("Romantika", "ltc::viss/romance","","Romantiskās filmas"),
  311. ("Piedzīvojumu", "ltc::viss/adventure","","Piedzivojumu filmas"),
  312. ("Supervaroņi", "ltc::viss/superheros","","Supervaroņu filmas"),
  313. ("Fantastika", "ltc::viss/scifi","","Fantastikas filmas"),
  314. ("Dokumentalās", "ltc::viss/documentaries","","Dokumentalās filmas"),
  315. ("Šausmu", "ltc::viss/horror","","Šausmu filmas"),
  316. ("Retro", "ltc::viss/retro_films","","Retro filmas"),
  317. ("Koncerti un karaoke", "ltc::viss/concerts_karaoke","","Koncerti un filmas"),
  318. ])
  319. ### Sērijas galvenā ###
  320. elif data == "videonoma/series":
  321. content.extend([
  322. ("Aktuālie seriāli", "ltc::viss/actual_series?series","","Nesen pievienotie seriāli"),
  323. ("Aktuālie šovi", "ltc::viss/actual_series?series","","Nesen pievienotie TV šovi"),
  324. ("Populārākie seriāli", "ltc::viss/popular_series?series","","Populārākie seriāli"),
  325. ("Dokumentalie seriāli", "ltc::viss/documentary?series","","Dokumentalie seriāli"),
  326. ("Izklaides pārraides", "ltc::viss/entertainment?series","","Izklaides pārraides"),
  327. ("Detektivseriāli", "ltc::viss/Crime_series?series","","Detektivseriāli"),
  328. ("Dramas seriāli", "ltc::viss/drama_series?series","","Dramas seriāli"),
  329. ("Par dabu", "ltc::viss/nature?series","","Seriāli par dabu"),
  330. ("Gardēžiem", "ltc::viss/gourmets?series","","Seriāli gardēžiem"),
  331. ("Mistērija", "ltc::viss/mystery_series?series","","Mistērijas seriāli"),
  332. ("Oriģinālsaturs", "ltc::viss/originals_series?series","","Oriģinālsatura seriāli"),
  333. ("Klasika", "ltc::viss/classic?series","","Klasika"),
  334. ])
  335. ### Bērnu galvenā ###
  336. elif data == "videonoma/bernu":
  337. content.extend([
  338. ("Nesen pievienotais", "ltc::viss/actual_kid","","Nesen pievienotais bernu saturs"),
  339. ("Bērnu Premiere", "ltc::viss/kids_premiere","","Bērnu Premiere saturs"),
  340. ("Multfilmas", "ltc::viss/animation2","","Multfilmas"),
  341. ("Bērnu seriāli", "ltc::viss/kids_series?series","","Bērnu seriāli"),
  342. ("Ģimenes filmas", "ltc::viss/family","","Ģimenes filmas"),
  343. ("Multfilmas latviski", "ltc::viss/animation_lv","","Multfilmas latviski"),
  344. ("Retro multfilmas", "ltc::viss/retro_animation","","Retro multfilmas"),
  345. ("Latviešu multfilmas", "ltc::viss/animation","","Latviešu multfilmas"),
  346. ("Raidijumi berniem", "ltc::viss/kids_events","","Raidijumi berniem"),
  347. ])
  348. ### Filmas premiere galvenā ###
  349. elif data == "videonoma/premiere":
  350. content.extend([
  351. ("Aktuālās Premiere filmas", "ltc::viss/actual_premiere","","Nesen pievienotās Premiere filmas"),
  352. ("Tikko no kinoteātra", "ltc::viss/just_from_cinema","","Filmas tikko no kinoteātra"),
  353. ("Bērnu Premiere", "ltc::viss/kids_premiere","","Bērnu Premiere saturs"),
  354. ("Komēdijas Premiere", "ltc::viss/comedy_premiere","","Premiere komēdijas filmas"),
  355. ("Detektivi Premiere", "ltc::viss/crime_premiere","","Premiere detektivfilmas"),
  356. ("Piedzīvojumu Premiere", "ltc::viss/adventure_premiere","","Premiere piedzivojumu filmas"),
  357. ("Fantastika Premiere", "ltc::viss/scifi_premiere","","Premiere fantastikas filmas"),
  358. ("Šausmu Premiere", "ltc::viss/horror_premiere","","Premiere šausmu filmas"),
  359. ])
  360. ### 360 Play galvenā ###
  361. elif data == "videonoma/360play":
  362. content.extend([
  363. ("Viss", "ltc::viss/bezmaksas?series","","Viss bezmaksas saturs"),
  364. ("360TV", "ltc::viss/360_free?series","","360TV bezmaksas saturs"),
  365. ("Red Bull", "ltc::viss/Red_bull?series","","Red Bull bezmaksas saturs"),
  366. ])
  367. r = self.call
  368. ### Videonomas saraksti ###
  369. elif plist[0] == "viss" or "load_more" in data:
  370. #content.append(("Videonomas saraksts", "", "", ""))
  371. if "viss" in data:
  372. r = self.call2(data)
  373. m = re.search('(<div class="container categorie">.+?)</div><div class="loader off">', r, re.DOTALL)
  374. html = m.group(1) if m else ""
  375. else:
  376. url = self.api_url2 + path
  377. params = data.split("?")[1]
  378. r = self._http_request(url, params, headers=self.headers3 )
  379. js = json.loads(r)
  380. html = js["data"] if "data" in r else ""
  381. result = re.findall("<div title=.+?</a>", html, re.DOTALL)
  382. for it in result:
  383. title1 = re.search('<div class="categorie-one-title">(.+?)<', it, re.DOTALL).group(1)
  384. title2 = re.search('<div class="categorie-one-subtitle">(.+?)<', it, re.DOTALL).group(1)
  385. if title1 <> title2:
  386. title = "%s ~ %s" % (title1, title2)
  387. else:
  388. title = title1
  389. img = self.api_url2 + re.search('img src="(.+?)"', it).group(1)
  390. data2 = re.search('href="/(.+?)/"', it).group(1)
  391. if "?series" in data or re.search("e\d+$", data2):
  392. data2 += "?series"
  393. if "Jaunums" in it:
  394. title += " [J]"
  395. if "Premiere" in it:
  396. title += " [P]"
  397. desc = title
  398. content.append((title,self.name+"::"+data2,img,desc))
  399. if not "end-reached-box" in r:
  400. if "load_more" in data:
  401. page = int(qs["pack"])
  402. data2 = re.sub("pack=\d+","pack=%s"%(page+1),data)
  403. else:
  404. section = re.search('data-section="(\d+)"', r).group(1)
  405. data2 = "load_more.json?pack=1&section=%s" % section
  406. content.append(("Next page",self.name+"::"+data2,"next.png","Go to next page"))
  407. return content
  408. ### Sērijas
  409. elif clist=="videonoma" and (params=="?series" or "season_nr" in qs):
  410. url = "https://www.shortcut.lv/"+path
  411. r = self._http_request(url,headers=self.headers2)
  412. if not r:
  413. return content
  414. m = re.search(r'data-movieid=\d+><div class="lv">([^<]+)</div><div[^>]+>([^<]+)<', r)
  415. if m:
  416. title1 = unescape(m.group(1))
  417. title2 = unescape(m.group(2))
  418. else:
  419. m = re.search('<meta name="dr:say:title" content="([^"]+)">', r, re.IGNORECASE)
  420. title1 = title2 = unescape(m.group(1)) if m else "Video"
  421. if 'episode_switcher_title' in r:
  422. set1 = title1.replace("\xe2\x80\x93", "-")
  423. if " - " in set1:
  424. set1 = set1.split(" - ")[0]
  425. set2 = title2.replace("\xe2\x80\x93", "-")
  426. if " - " in set2:
  427. set2 = set2.split(" - ")[0]
  428. if set1 <> set2:
  429. raidijums = "%s ~ %s" % (set1, set2)
  430. else:
  431. raidijums = set1
  432. else:
  433. m = re.search('<div class="movie_titles"><div class="en">([^<]+)</div>', r)
  434. raidijums = m.group(1) if m else "Series"
  435. img0 = re.search('<meta name="og:image" content="([^"]+)">', r).group(1) if re.search('<meta name="dr:say:img" content="([^"]+)">', r) else ""
  436. m = re.search('season_choice', r)
  437. # Ir sezonas
  438. if m:
  439. result = re.findall(r"""season_choice\(.+?'epizode','([^']*)',[^,]+,'(\d+)',[^,]+,[^,]+\)">([^<]+)<""", r, re.MULTILINE)
  440. if not result:
  441. raise Exception("No seasons find!")
  442. vid = result[0][0]
  443. if not "season_nr" in qs: # Sezonu saraksts
  444. for s,it in enumerate(result):
  445. title = "%s - %s" % (raidijums, it[2])
  446. data2 = path+"?season_nr=%s"%(it[1])
  447. img = img0
  448. desc = title
  449. content.append((title, self.name + "::" + data2, img, desc))
  450. #return content
  451. else: # Sezonas epizožu saraksts
  452. if not "season_nr" in qs:
  453. qs["season_nr"]="0"
  454. # https://www.shortcut.lv/api/episode-loader-design17/0?chunk_size=4&sorting=epizode&series_id=dora_the_explorer
  455. # https://www.shortcut.lv/api/episode-loader-design17/3?chunk_size=4&sorting=epizode&series_id=dora_the_explorer&movie_id=10161&z360tv=&is_active_season=false HTTP/1.1
  456. # https://www.shortcut.lv/api/episode-loader-design17/3?chunk_size=4&sorting=epizode&series_id=dora_the_explorer
  457. url = "https://www.shortcut.lv/api/episode-loader-design17/%s?chunk_size=4&sorting=epizode&series_id=%s" % (qs["season_nr"], vid)
  458. r = self._http_request(url,headers=self.headers2)
  459. try:
  460. js = json.loads(r)
  461. except:
  462. raise Exception("Error getting episode list")
  463. result = re.findall(r'class="" href="([^"]+)".+?image:url\(([^ \)]+)\); "></div><div class="episode_titlez_design17">([^<]+)</div><div class="episode_number_design17">([^<]+)</div></a>', js["data"])
  464. for item in result:
  465. title = "%s - %s (%s)"%(raidijums,item[2],item[3])
  466. data2 = item[0][1:]
  467. img = "https://www.shortcut.lv"+item[1]
  468. desc = title
  469. content.append((title,self.name+"::"+data2,img,desc))
  470. #return content
  471. # Nav sezonu
  472. else:
  473. result = re.findall(r'class="" href="([^"]+)".+?image:url\(([^ \)]+)\).+?class="episode_titlez_design17">([^<]+)</div><div class="episode_number_design17">([^<]+)</div></a>', r)
  474. for item in result:
  475. title = "%s - %s(%s)"%(raidijums,item[2],item[3])
  476. data2 = item[0][1:]
  477. img = "https://www.shortcut.lv"+item[1]
  478. desc = title
  479. content.append((title,self.name+"::"+data2,img,desc))
  480. #return content
  481. if not result: # nav ne epizožu, atgriež pats sevi bez ?series
  482. title = desc = raidijums
  483. img = img0
  484. data2 = path
  485. content.append((title,self.name+"::"+data2,img,desc))
  486. return content
  487. ### Videonomas video
  488. elif clist=="videonoma" and len(data.split("/"))> 2:
  489. ch = data.split("/")[1]
  490. video_id=data[data.find("/")+1:]
  491. #video_id=data.split("/")[-1]
  492. if not self.is_logedin2():
  493. if not self.login2():
  494. content=("Can not login\nPlease check lattelecom.tv username/password in\n/usr/lib/enigma2/python/Plugins/Extensions/sources/ltc.cfg file","","","No stream found. Please check lattelecom.tv username/password in sources/ltc.cfg file ")
  495. return content
  496. data2 = self.get_noma_url(video_id)
  497. if not data2:
  498. content=("No stream found for '%s'"%data,"","","No stream found")
  499. return content
  500. r = self.call2(data)
  501. m = re.search('<meta name="dr:say:title" content="([^"]+)">', r, re.IGNORECASE)
  502. if m:
  503. title = m.group(1)
  504. else:
  505. title = ""
  506. desc = title
  507. content = (title,data2,"",desc)
  508. return content
  509. return content
  510. def get_streams(self, data):
  511. print "[ltc] get_streams:", data
  512. if not self.is_video(data):
  513. return []
  514. source, data, path, plist, clist, params, qs = self.parse_data(data)
  515. #if "::" in data: data = data.split("::")[1]
  516. ### Video nomas strīmus pagaidām dabu no mājas lapas TODO
  517. if data.split("/")[0]=="videonoma" and len(data.split("/"))>1:
  518. #video_id=data.split("/")[-1]
  519. streams = self.get_stream_url2(data)
  520. else:
  521. if not self.is_logedin():
  522. if not self.login():
  523. return []
  524. if plist[1] == "catchup":
  525. start = int(qs["start"]) + 1
  526. #url = "https://manstv.lattelecom.tv/api/v1.7/get/content/epgs/?filter[channel]=101&filter[utFrom]=1551551500&include=channel&page[size]=40page[number]=1"
  527. data2 = "content/epgs/?filter[channel]=%s&filter[utFrom]=%s&filter[utTo]=%s&include=channel&page[size]=40page[number]=1" % (plist[2], start, start )
  528. r2 = self.call(data2)
  529. if "data" in r2:
  530. epgid = r2["data"][0]["id"]
  531. else:
  532. return []
  533. data = "ltc::content/record-streams/%s?include=quality" % epgid
  534. source, data, path, plist, clist, params, qs = self.parse_data(data)
  535. data = data + "&auth_token=app_" + self.token
  536. headers = self.headers
  537. headers["Authorization"] = "Bearer " + self.token
  538. r = self.call(data, headers=headers)
  539. if not r: return []
  540. if "errors" in r:
  541. return []
  542. #self.refresh_token()
  543. #token = "&auth_token=app_%s"%(self.token)
  544. vid = data.split("/")[2].split("?")[0]
  545. vtype = data.split("/")[1]
  546. if vtype == "live-streams":
  547. ch = self.get_channel_by_id(vid)
  548. title = ch["attributes"]["title"]
  549. now = dt2ts(dt.now())
  550. r2 = self.call("content/epgs/?filter[channel]=%s&filter[utFrom]=%s&filter[utTo]=%s&include=channel" % (vid, now, now))
  551. epg = r2["data"][0]["attributes"] if len(r2["data"]) > 0 else None
  552. if epg:
  553. start = ts2dt(int(epg["unix-start"]))
  554. start = start.strftime("%H:%M")
  555. end = ts2dt(int(epg["unix-stop"]))
  556. end = end.strftime("%H:%M")
  557. title = "%s - %s [%s-%s]"%(title,epg["title"],start,end)
  558. desc = title
  559. if epg["description"]:
  560. desc += "\n" + epg["description"]
  561. img = "https://manstv.lattelecom.tv/"+ epg["poster-url"]
  562. else:
  563. img = "https://manstv.lattelecom.tv/"+ ch["attributes"]["logo-url"]
  564. desc = ch["attributes"]["description"]
  565. elif vtype == "record-streams":
  566. epg = self.get_epg_id(vid)
  567. if epg:
  568. title = epg["title"].encode("utf8")
  569. t1 = datetime.datetime.fromtimestamp(int(epg["unix_start"])).strftime('%H:%M')
  570. t2 = datetime.datetime.fromtimestamp(int(epg["unix_stop"])).strftime('%H:%M')
  571. date = epg["date"]
  572. #title = "%s [%s %s-%s]"%(title,date,t1,t2)
  573. desc = title + "\n" + epg["description"].encode("utf8")
  574. img = epg["img"].encode("utf8") if "img" in epg else ""
  575. else:
  576. title = desc = data
  577. img = ""
  578. streams = []
  579. for s in r["data"]:
  580. stream = util.item()
  581. stream["url"]=(s["attributes"]["stream-url"]).encode("utf8")
  582. stream["name"]=title
  583. stream["desc"]=desc
  584. stream["img"] = img
  585. stream["type"]="hls"
  586. m = re.search(".+_(\w\w)_(\w\w)\.\w+",s["id"])
  587. if m:
  588. stream["quality"]=m.group(2).encode("utf8")
  589. stream["lang"]=m.group(1).encode("utf8")
  590. streams.append(stream)
  591. ### TODO - sakārtot sarakstu, lai pirmais ir labakais video
  592. qlist = ["???","lq","mq","hq","hd"]
  593. llist = ["fr","en","ru","lv"]
  594. for s in streams:
  595. lv = llist.index(s["lang"])*10 if s["lang"] in llist else 0
  596. qv=qlist.index(s["quality"]) if s["quality"] in qlist else 0
  597. s["order"] = lv+qv
  598. streams = sorted(streams,key=lambda item: item["order"],reverse=True)
  599. return streams
  600. def is_video(self,data):
  601. source, data, path, plist, clist, params, qs = self.parse_data(data)
  602. if "get-stream" in data:
  603. return True
  604. if clist in ("live-streams","record-streams","vod-streams", "catchup"):
  605. return True
  606. elif cmd[0]=="arhivs" and len(cmd)==4:
  607. return True
  608. elif cmd[0]=="videonoma" and len(cmd) >= 3 and not "?" in data:
  609. return True
  610. else:
  611. return False
  612. def get_channels(self):
  613. if self.ch:
  614. return self.ch
  615. #if not self.check_logedin():
  616. # self.login() # citādi nerāda TV3, LNT, TV6
  617. r= self.call("channels?page[size]=1000&&filter[state]=1")
  618. self.ch=[]
  619. for i,item in enumerate(r["data"]):
  620. self.ch.append(item)
  621. self.ch_id[item["id"]]=i
  622. self.ch_name[item["attributes"]["title"]]=i
  623. return self.ch
  624. def get_channel_by_id(self,chid):
  625. if not self.ch:
  626. self.get_channels()
  627. if not self.ch:
  628. return None
  629. return self.ch[self.ch_id[chid]] if self.ch_id.has_key(chid) else None
  630. def get_channel_by_id2(self,chid):
  631. if not self.ch:
  632. self.get_channels()
  633. if not self.ch:
  634. return None
  635. return self.ch[self.ch_id2[chid]] if self.ch_id2.has_key(chid) else None
  636. def get_channels2(self):
  637. if self.ch2:
  638. return self.ch2
  639. r= self.call2("tiesraide")
  640. self.ch2=[]
  641. for item in re.findall(r'<div class="entry forward-link.+?data-xprs="(\d+)".+?title="([^"]+)" href="/tiesraide/([^"]+)"', r, re.DOTALL):
  642. ch={}
  643. ch["id2"]=item[0]
  644. ch["name"]=item[2]
  645. ch["title"]=item[1]
  646. self.ch2.append(ch)
  647. return self.ch2
  648. def get_channel_by_name2(self,name):
  649. if not self.ch2:
  650. self.get_channels2()
  651. for c in self.ch2:
  652. if c["name"]==name:
  653. return c
  654. return None
  655. def get_channel_by_name(self,name):
  656. if not self.ch:
  657. self.get_channels()
  658. ch2 = self.get_channel_by_name2(name)
  659. if not ch2:
  660. return None
  661. ch = self.get_channel_by_id2(ch2["id2"])
  662. return ch
  663. def get_epg(self,date=None,update=False):
  664. #https://manstv.lattelecom.tv/api/v1.3/get/tv/epg/?daynight=2016-05-19
  665. today=datetime.date.today()
  666. if not date:
  667. date=datetime.date.today().strftime("%Y-%m-%d")
  668. if update:
  669. self.epg=[]
  670. self.epg_id={}
  671. self.epg_id2={}
  672. self.epg_date={}
  673. self.epg_ch={}
  674. self.epg_cat={}
  675. if not date in self.epgdates:
  676. r=self.call("tv/epg/?daynight=%s"%date)
  677. for item in r["items"]:
  678. if item["id"] in self.epg_id: continue
  679. else:
  680. item["time_start2"] = datetime.datetime.fromtimestamp(int(item["unix_start"]))
  681. item["time_stop2"] = datetime.datetime.fromtimestamp(int(item["unix_stop"]))
  682. item["date"]=item["time_start2"].strftime("%Y-%m-%d")
  683. self.epg.append(item)
  684. index = self.epg.index(item)
  685. self.epg_id[item["id"]]=index
  686. if not item["date"] in self.epg_date:
  687. self.epg_date[item["date"]]=[]
  688. self.epg_date[item["date"]].append(index)
  689. if not item["channel_id"] in self.epg_ch:
  690. self.epg_ch[item["channel_id"]]=[]
  691. self.epg_ch[item["channel_id"]].append(index)
  692. if not date in self.epgdates:
  693. self.epgdates.append(date)
  694. def get_epg_id(self,epgid):
  695. if self.epg and epgid in self.epg_id:
  696. return self.epg[self.epg_id[epgid]]
  697. else:
  698. r=self.call("content/epgs/%s" % epgid)
  699. if not r:
  700. return None
  701. item = r["data"][0]["attributes"]
  702. item["unix_start"] = item["unix-start"]
  703. item["unix_stop"] = item["unix-stop"]
  704. item["time_start2"] = datetime.datetime.fromtimestamp(int(item["unix_start"]))
  705. item["time_stop2"] = datetime.datetime.fromtimestamp(int(item["unix_stop"]))
  706. item["date"]=item["time_start2"].strftime("%Y-%m-%d")
  707. item["img"] = "https://manstv.lattelecom.tv/" + item["url"]
  708. return item
  709. def get_epg_date(self,date,chid):
  710. if not date in self.epgdates:
  711. self.get_epg(date)
  712. items = []
  713. ch = self.get_channel_by_id(chid)
  714. for it in self.epg_date[date]:
  715. item = self.epg[it]
  716. if item["channel_id"]==ch["xprs_id"]:
  717. items.append(item)
  718. items.reverse()
  719. return items
  720. def call(self, data,params = None, headers=None):
  721. if not headers: headers = self.headers
  722. #if not lang: lang = self.country
  723. url = self.api_url + data
  724. content = self._http_request(url,params, headers)
  725. if content:
  726. try:
  727. result = json.loads(content)
  728. return result
  729. except Exception, ex:
  730. return None
  731. else:
  732. return None
  733. def call2(self, data,params = None, headers=None):
  734. if not headers: headers = self.headers2
  735. #if not lang: lang = self.country
  736. url = self.api_url2 + data
  737. content = self._http_request(url,params, headers)
  738. return content
  739. def _http_request0(self, url,params = None, headers=None):
  740. if not headers: headers = self.headers
  741. try:
  742. r = urllib2.Request(url, data=params, headers=headers)
  743. u = urllib2.urlopen(r)
  744. content = u.read()
  745. u.close()
  746. return content
  747. except Exception as ex:
  748. return None
  749. def login(self,user="",password=""):
  750. """Login in to site, get token"""
  751. self.options_read()
  752. if not user: user=self.options["user"]
  753. if not password: password = self.options["password"]
  754. # Dabūjam tokenu
  755. url = "https://manstv.lattelecom.tv/api/v1.7/post/user/users/%s" % user
  756. #url = "https://manstv.lattelecom.tv/api/v1.5/post/user/login"
  757. params = "uid=5136baee575056945136baee57505694&password=%s&" % (password)
  758. #params = "uid=7f777e938d35e017&password=%s&username=%s&"%(password,user)
  759. headers = headers2dict("""
  760. User-Agent: Shortcut.lv v2.9.1 / Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G900FD Build/KOT49H)
  761. Content-Type: application/x-www-form-urlencoded; charset=UTF-8
  762. Host: manstv.lattelecom.tv
  763. """ )
  764. try:
  765. r = urllib2.Request(url, data=params, headers=headers)
  766. u = urllib2.urlopen(r)
  767. content = u.read()
  768. u.close()
  769. except Exception as ex:
  770. return None
  771. #r = self.call(data, params)
  772. if r and "token" in content:
  773. self.token=re.search('"token":"(.+?)"', content).group(1)
  774. return True
  775. else:
  776. return False
  777. def refresh_token(self):
  778. url = "https://manstv.lattelecom.tv/api/v1.7/post/user/refresh-token/"
  779. params = "uid=5136baee57505694&token=%s" % self.token
  780. headers = self.headers
  781. headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"
  782. r = self._http_request(url, params=params, headers=headers)
  783. r = json.loads(r)
  784. if 'token' in r["data"]["attributes"]:
  785. self.token = r["data"]["attributes"]["token"]
  786. return True
  787. else:
  788. self.token = ""
  789. return False
  790. def check_logedin(self):
  791. if not self.token:
  792. return False
  793. else:
  794. if self.refresh_token():
  795. return True
  796. else:
  797. return False
  798. def is_logedin(self):
  799. if self.token:
  800. return True
  801. else:
  802. return False
  803. #----------------------------------------------------------------------
  804. def get_tv_url(self,video_id):
  805. """Get m3u8 url for given live tv channel"""
  806. url = "https://m.lattelecom.tv/tiesraide/%s"%video_id
  807. data = self.get_stream_url2("tiesraide/%s"%video_id)
  808. #headers = self.headers
  809. #headers["Cookie"] = "%s; %s; _hjIncludedInSample=0; MobBitr=1; MobRentBitr=%s; MobRentLang=%s;"%(self.session_id,self.mobtv_cache,data["quality"],data["language"])
  810. #response = urllib2.urlopen(urllib2.Request(url, headers=headers))
  811. #html = response.read()
  812. #m3u8 = re.search('x-mpegURL" src="(.*?)"', html).group(1) if "x-mpegURL" in html else ""
  813. return data["stream"] if data else None
  814. #----------------------------------------------------------------------
  815. def get_noma_url(self,video_id,quality="hq",language="lv"):
  816. """Get m3u8 url for given rental video"""
  817. video_id1,video_id2=video_id.split("/")
  818. data = self.get_stream_url2(video_id)
  819. #url = "https://m.lattelecom.tv/free_origin?show_origin=1&type=4&video_url=%s&bitrate=%s&lng=%s"%(video_id2,data["quality"],data["language"])
  820. #headers = self.headers
  821. #headers["Cookie"] = "%s; %s; MobRentBitr=%s; MobBitr=1; MobRentLang=%s"%(self.session_id,self.mobtv_cache,quality,language)
  822. #response = urllib2.urlopen(urllib2.Request(url, headers=headers))
  823. #m3u8 = response.read()
  824. return data["stream"] if data else None
  825. #----------------------------------------------------------------------
  826. def get_arhivs_url(self,video_id):
  827. """Get m3u8 url for given archive video"""
  828. data = self.get_video_data(video_id)
  829. #url = "https://m.lattelecom.tv/free_origin?show_origin=1&type=arhivs&event_id=%s&bitrate=mhq"%(video_id) #,data["quality"])
  830. #headers = self.headers
  831. #headers["Cookie"] = "%s; %s; MobBitr=1; "%(self.session_id,self.mobtv_cache)
  832. #response = urllib2.urlopen(urllib2.Request(url, headers=headers))
  833. #m3u8 = response.read()
  834. return data["stream"] if data else None
  835. def get_stream_url(self,video_id,type="vod",chid=""):
  836. "Get stream urls (hls) using manstv api"
  837. if type=="vod":
  838. data = "vod/get-stream/%s"%video_id
  839. elif type=="tv":
  840. data = "tv/get-stream?id=%s"%video_id
  841. elif type=="archive":
  842. if not chid: return None
  843. data="archive/get-stream/%s?channelid=%s"%(video_id,chid)
  844. else:
  845. return None
  846. if not self.is_logedin():
  847. if not self.login():
  848. return None
  849. r = self.call(data)
  850. if not r:
  851. return None
  852. self.refresh_token()
  853. token = "&auth_token=app_%s"%(self.token)
  854. hls={}
  855. hls["stream"]=r["stream"]+token
  856. hls["streams"]=[]
  857. if "items" in r:
  858. for s in r["items"]["streams"]:
  859. s["stream"] += token
  860. hls["streams"].append(s)
  861. return hls
  862. def get_info2(self, data):
  863. # Get movie info (for VOD)
  864. nfo = {}
  865. tt = lambda p,r,d: re.search(p,r).group(1) if re.search(p,r) else d
  866. tt2 = lambda p,r,d: (re.sub("[\.]","",re.search(p,r).group(1))).split(", ") if re.search(p,r) else d
  867. r = self.call2(data)
  868. m = re.search(r'data-movieid=\d+><div class="lv">([^<]+)</div><div[^>]+>([^<]+)<', r)
  869. if m:
  870. title1 = unescape(m.group(1))
  871. title2 = unescape(m.group(2))
  872. else:
  873. m = re.search('<meta name="dr:say:title" content="([^"]+)">', r, re.IGNORECASE)
  874. title1 = title2 = unescape(m.group(1)) if m else "Video"
  875. nfo["title"] = title1
  876. if not title1 == title2:
  877. nfo["originaltitle"] = title2
  878. if nfo["originaltitle"] and not nfo["title"]:
  879. nfo["title"] = nfo["originaltitle"]
  880. if 'episode_switcher_title' in r:
  881. set1 = title1.replace("\xe2\x80\x93", "-")
  882. if " - " in set1:
  883. set1 = set1.split(" - ")[0]
  884. set2 = title2.replace("\xe2\x80\x93", "-")
  885. if " - " in set2:
  886. set2 = set2.split(" - ")[0]
  887. if set1 <> set2:
  888. nfo["set"] = "%s ~ %s" % (set1, set2)
  889. m = re.search(r"<br>(S(\d+)E(\d+))<", r, re.IGNORECASE | re.DOTALL)
  890. if m:
  891. #nfo["title"] = nfo["title"] + " (%s)" % m.group(1)
  892. #nfo["originaltitle"] = nfo["originaltitle"] + " (%s)" % m.group(1)
  893. nfo["season"] = m.group(2)
  894. nfo["episode"] = m.group(2)
  895. nfo["thumb"] = tt('<meta name="og:image" content="([^"]+)"', r,"")
  896. nfo["thumb"] = nfo["thumb"].replace("http:", "https:")
  897. nfo["year"] = tt('movie-informatio-title">Gads<.+?content">([^<]+)<', r, "")
  898. nfo["runtime"] = tt('movie-informatio-title">Garums<.+?content">([^<]+)<', r, "")
  899. nfo["quality"] = tt('movie-informatio-title">Kvalitāte<.+?content">([^<]+)<', r, "")
  900. nfo["genre"]=tt('movie-informatio-title">Žanrs<.+?content">([^<]+)<', r, "")
  901. nfo["director"] = tt('movie-informatio-title">Režisor.<.+?content">([^<]+)<', r, "")
  902. nfo["actor"] = tt2('class="movie-informatio-content actors">([^<]+)<', r, "")
  903. nfo["language"] = tt2('movie-informatio-title">Valodas*<.+?content">([^<]+)<', r, "")
  904. nfo["subtitles"] = tt2('movie-informatio-title">Subtitri<.+?content">([^<]+)<', r, "")
  905. nfo["plot"] = tt('class="movie-informatio-content introduction">([^<]+)<', r, "")
  906. nfo["tagline"] = nfo["plot"]
  907. return nfo
  908. def get_stream_url2(self,data):
  909. video_id=data[data.find("/")+1:]
  910. if not self.is_logedin2():
  911. if not self.login2():
  912. return []
  913. #data2 = self.get_noma_url(video_id)
  914. nfo = self.get_info2(data)
  915. title = util.nfo2title(nfo)
  916. desc = util.nfo2desc(nfo)
  917. img = nfo["thumb"]
  918. #xml = util.nfo2xml(nfo)
  919. url = "https://www.shortcut.lv/xmls/%s.xml"%video_id
  920. headers = self.headers2
  921. headers["Cookie"] = self.session_id
  922. response = urllib2.urlopen(urllib2.Request(url, headers=headers))
  923. r = response.read()
  924. servers = re.findall("(?s)<origin>([^<]+)</origin>", r)
  925. streams_xml = re.findall('<stream quality="\w+">(mp4:\w+(\w\w)_(\w\w).mp4)</stream>',r)
  926. if not streams_xml:
  927. raise Exception("No stream, buy movie?")
  928. resource_id = re.search("(?s)<resource_id>([^<]+)</resource_id>", r).group(1)
  929. token = re.search("(?s)<auth_token>([^<]+)</auth_token>", r).group(1)
  930. streams=[]
  931. captions = []
  932. llist = ["fr","en","ru","lv"]
  933. for s in re.findall('<subtitles code="([^"]+)">([^<]+)</subtitles>', r, re.DOTALL):
  934. sub = {}
  935. sub["url"] = s[1]
  936. sub["lang"] = s[0]
  937. sub["name"] = "captions (vtt)"
  938. sub["type"] = "vtt"
  939. sub["order"] = llist.index(sub["lang"])*10 if sub["lang"] in llist else 0
  940. captions.append(sub)
  941. captions = sorted(captions,key=lambda item: item["order"],reverse=True)
  942. for s in streams_xml:
  943. for server in servers:
  944. stream = util.item()
  945. server2 = self.load_balancer(server)
  946. url = "http://%s/mobile-vod/%s/playlist.m3u8?resource_id=%s&auth_token=%s"%(server2,s[0],resource_id,token)
  947. # TODO Engima2 gstreamer vajag lai padod playlist nevis chunklist
  948. # r3 = self._http_request(url)
  949. # sss = re.findall(r"#EXT-X-STREAM-INF:.*?BANDWIDTH=(\d+).*?\n(.+?)$", r3, re.IGNORECASE | re.MULTILINE)
  950. # if sss:
  951. # url2 = sss[0][1]
  952. # stream["headers"] = {"Referer":url}
  953. # if url2.startswith("http"):
  954. # url = url2
  955. # else:
  956. # url = util.hls_base(url)+url2
  957. stream["url"]=url
  958. stream["lang"]=s[1]
  959. stream["quality"]=s[2]
  960. stream["name"]= unescape(title)
  961. stream["desc"]= unescape(desc)
  962. stream["img"] = img
  963. stream["nfo"] = {"movie":nfo}
  964. stream["type"]="hls" #stream_type(url)
  965. stream["subs"] = []
  966. for c in captions:
  967. c2= c.copy()
  968. #c2["url"] ="http://%s/mobile-vod/%s/%s?resource_id=%s&auth_token=%s"%(server2,s[0],c["url"],resource_id,token)
  969. c2["url"] ="http://%s/mobile-vod/%s/%s"%(server2,s[0],c["url"])
  970. stream["subs"].append(c2)
  971. pass
  972. streams.append(stream)
  973. break # TODO ņem tikai pirmo serveri, varētu pārbaudit, kurš no tiem strādā, kurš ne
  974. return streams
  975. #data = {}
  976. #data["server"] = re.findall("(?s)<origin>([^<]+)</origin>", r)[1]
  977. #data["language"]=re.findall('(?s)<language code="([^"]+)">', r)
  978. #data["language"]="lv" if "lv" in data["language"] else "ru" if "ru" in data["language"] else "en"
  979. #data["qs"]=re.findall('(?s)<stream quality="([^"]+)">([^<]+)</stream>', r)
  980. #data["qs"]=dict(data["qs"])
  981. #qs = data["qs"].keys()
  982. #data["quality"] = "hd" if "hd" in qs else "hq" if "hq" in qs else "mhq" if "mhq" in qs else "lq"
  983. #data["mp4"] = data["qs"][data["quality"]]
  984. #data["token"] = re.search("(?s)<auth_token>([^<]+)</auth_token>", r).group(1)
  985. #data["resource_id"]=re.search("(?s)<resource_id>([^<]+)</resource_id>", r).group(1)
  986. #data["server"]=self.load_balancer(data["server"])
  987. #data["hls"] = "http://%s/mobile-vod/%s/playlist.m3u8?resource_id=%s&auth_token=%s"%(data["server"],data["mp4"],data["resource_id"],data["token"])
  988. ##data["hls"]="http://%s/mobile-vod/mp4:%s/playlist.m3u8?resource_id=%s&auth_token=app_%s"%(data["server"],data["mp4"],data["resource_id"],data["token"])
  989. ### wwww.shortcut.lv izsaukumi
  990. def login2(self,user="",password=""):
  991. """Login in to site, create session cookies"""
  992. if not user: user=self.options["user"]
  993. if not password: password = self.options["password"]
  994. url0 = "https://www.shortcut.lv"
  995. class NoRedirectHandler(urllib2.HTTPRedirectHandler):
  996. def http_error_302(self, req, fp, code, msg, headers):
  997. infourl = urllib.addinfourl(fp, headers, req.get_full_url())
  998. infourl.status = code
  999. infourl.code = code
  1000. return infourl
  1001. http_error_300 = http_error_302
  1002. http_error_301 = http_error_302
  1003. http_error_303 = http_error_302
  1004. http_error_307 = http_error_302
  1005. # Dabūjam sesijas id un url_gif, kas redirektējas uz auth_url
  1006. headers = headers2dict("""
  1007. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
  1008. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  1009. Accept-Language: en-US,en;q=0.5
  1010. DNT: 1
  1011. Connection: keep-alive
  1012. """)
  1013. response = urllib2.urlopen(urllib2.Request(url0, headers=headers))
  1014. session_id = response.headers["set-cookie"].split(";")[0]
  1015. html = response.read()
  1016. url_gif = url0 + re.search('(/auth/\d+\.gif)', html).group(1)
  1017. # Dabūtjam auth_url
  1018. headers = headers2dict("""
  1019. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
  1020. Accept: image/png,image/*;q=0.8,*/*;q=0.5
  1021. Accept-Language: en-US,en;q=0.5
  1022. DNT: 1
  1023. Referer: https://www.shortcut.lv/
  1024. """)
  1025. headers["Cookie"] = session_id
  1026. urllib2.install_opener(urllib2.build_opener(NoRedirectHandler()))
  1027. response = urllib2.urlopen(urllib2.Request(url_gif, headers=headers))
  1028. if response.code == 302:
  1029. url_auth = response.headers["location"]
  1030. else:
  1031. self.error = u"auth.gif nenostrādāja"
  1032. raise Exception(u"auth.gif nenostrādāja")
  1033. #return False
  1034. # Pierakstāmies iekš auth.lattelecom.lv
  1035. headers = headers2dict("""
  1036. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
  1037. Accept: image/png,image/*;q=0.8,*/*;q=0.5
  1038. Accept-Language: en-US,en;q=0.5
  1039. DNT: 1
  1040. Referer: https://www.shortcut.lv/
  1041. """)
  1042. response = urllib2.urlopen(urllib2.Request(url_auth, headers=headers))
  1043. if not response.code == 302:
  1044. self.error = u"pierakstīšanās auth.lattelecom.lv nenostrādāja"
  1045. return False
  1046. #raise "pierakstīšanās auth.lattelecom.lv nenostrādāja"
  1047. # Mēģinam ielogoties
  1048. headers = headers2dict("""
  1049. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
  1050. Accept: text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01
  1051. Accept-Language: en-US,en;q=0.5
  1052. DNT: 1
  1053. X-Requested-With: XMLHttpRequest
  1054. Referer: https://www.shortcut.lv/
  1055. """)
  1056. headers["Cookie"] = session_id
  1057. #data = "login=yes&email=%s&passw=%s"%(user,password)
  1058. url = "https://www.shortcut.lv/login.json?callback=jQuery111303344749731668816_1463817318435&username=%s&password=%s&captcha=&sid=&_="%(user,password)
  1059. req = urllib2.Request(url, headers=headers)
  1060. response = urllib2.urlopen(req)
  1061. #with open("auth.htm","w") as f: f.write(response.read())
  1062. if not response.code == 200:
  1063. #self.error = u"kļūda ielogojoties"
  1064. raise Exception(u"kļūda ielogojoties")
  1065. html = response.read()
  1066. if not '"success":true' in html:
  1067. err = re.search('"error":"(.+?)"',html).group(1) if re.search('"error":"(.+?)"',html) else ""
  1068. raise Exception(u"Kļūda ielogojoties - %s"%err.decode("utf8"))
  1069. self.session_id = session_id
  1070. self.headers2["Cookie"] = "%s; "%(self.session_id)
  1071. self.error = ""
  1072. return True
  1073. def check_logedin2(self):
  1074. if not self.session_id:
  1075. return False
  1076. else:
  1077. url = "https://www.shortcut.lv/profils"
  1078. response = urllib2.urlopen(urllib2.Request(url, headers=self.headers2))
  1079. if response.code == 200:
  1080. return True
  1081. else:
  1082. self.session_id = ""
  1083. return False
  1084. def is_logedin2(self):
  1085. if self.session_id:
  1086. return True
  1087. else:
  1088. return False
  1089. def load_balancer(self,server,streams=[]):
  1090. headers = headers2dict("""
  1091. Connection: keep-alive
  1092. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
  1093. X-Requested-With: ShockwaveFlash/21.0.0.242
  1094. """)
  1095. url = "http://%s/loadbalancer"%server
  1096. response = urllib2.urlopen(urllib2.Request(url, headers=headers))
  1097. r = response.read()
  1098. statuss = re.search("<status>(\d+)</status>",r).group(1)
  1099. edge = re.search("<edge>([^<>]+)</edge>",r).group(1)
  1100. return edge
  1101. def test_hls(self,url):
  1102. headers = headers2dict("""
  1103. Connection: keep-alive
  1104. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
  1105. """)
  1106. try:
  1107. response = urllib2.urlopen(urllib2.Request(url, headers=headers))
  1108. except Exception as ex:
  1109. print "hls failed: %s %s"%(ex.getcode(),url)
  1110. return False
  1111. if response.code == 200:
  1112. html = response.read()
  1113. url0 = re.search("(http://[^/]+/)",url).group(1)
  1114. chunklist = re.search("(chunklist.+)",html).group(1).strip()
  1115. url2 = url.split('/')[:-1]
  1116. url2 ="/".join(url2)+"/"+chunklist
  1117. try:
  1118. response2 = urllib2.urlopen(urllib2.Request(url2, headers=headers))
  1119. except Exception as ex:
  1120. print "hls chunk failed: %s %s"%(ex.getcode(),url2)
  1121. return False
  1122. if response2.code == 200:
  1123. return True
  1124. else:
  1125. return False
  1126. else:
  1127. return False
  1128. if __name__ == "__main__":
  1129. sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
  1130. import run
  1131. source = Source()
  1132. data= sys.argv[1] if len(sys.argv)>1 else source.name+"::home"
  1133. run.run(source, data)
  1134. sys.exit()
  1135. c.get_info2(sys.argv[1])
  1136. if len(sys.argv)>1 and not "ltc::" in sys.argv[1]:
  1137. vid = vid2 = sys.argv[1]
  1138. password = sys.argv[2]
  1139. print "login - %s"%c.login("ivars777",password)
  1140. #vid = "1069"
  1141. #vid = "1462566072086"
  1142. #channelid="101"
  1143. #vid = "1350462656767"
  1144. #data = c.get_stream_url(vid,"vod")
  1145. #call([r"c:\Program Files\VideoLAN\VLC\vlc.exe",data["stream"]])
  1146. #pass
  1147. print "login2 - %s"%c.login2("ivars777",password)
  1148. #vid2 = "animation/ultimate_avengers_ii"
  1149. #vid2 = "animation/ice_age"
  1150. #vid2 = "tiesraide/ltv1"
  1151. #vid2 = "arhivs/1456521417815"
  1152. streams = c.get_stream_url2(vid2)
  1153. stream = streams[-1]
  1154. print
  1155. #for s in data:
  1156. #call([r"c:\Program Files\VideoLAN\VLC\vlc.exe",stream["url"]])
  1157. cmd = ["ffplay.exe",
  1158. "-headers","Referer:%s"%(quote(stream["headers"]["Referer"])),
  1159. "-headers","Connection:Keep-Alive",
  1160. stream["url"]]
  1161. print " ".join(cmd)
  1162. call(cmd)
  1163. pass
  1164. else:
  1165. if len(sys.argv)>1:
  1166. data= sys.argv[1]
  1167. else:
  1168. data = "ltc::home"
  1169. content = c.get_content(data)
  1170. for item in content:
  1171. print item
  1172. #cat = api.get_categories(country)
  1173. #chan = api.get_channels("lv")
  1174. #prog = api.get_programs(channel=6400)
  1175. #prog = api.get_programs(category=55)
  1176. #seas = api.get_seasons(program=6453)
  1177. #str = api.get_streams(660243)
  1178. #res = api.get_videos(802)
  1179. #formats = api.getAllFormats()
  1180. #det = api.detailed("1516")
  1181. #vid = api.getVideos("13170")
  1182. pass