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

PlayStream.py 39KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. #!/usr/bin/env python
  2. # coding=utf8
  3. #
  4. #
  5. # This file is part of PlayStream - enigma2 plugin to play video streams from various sources
  6. # Copyright (c) 2016 ivars777 (ivars777@gmail.com)
  7. # Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
  8. # Used fragments of code from enigma2-plugin-tv3play by Taapat (https://github.com/Taapat/enigma2-plugin-tv3play)
  9. #
  10. import os,time,sys,os,os.path
  11. import datetime,re
  12. import urllib2
  13. from enigma import ePicLoad, eServiceReference, eTimer, getDesktop
  14. from Components.ActionMap import ActionMap,HelpableActionMap
  15. from Screens.InfoBarGenerics import InfoBarShowHide
  16. from Components.AVSwitch import AVSwitch
  17. from Components.Label import Label
  18. from Components.MenuList import MenuList
  19. from Components.Pixmap import Pixmap
  20. from Components.Sources.List import List
  21. from Components.Sources.StaticText import StaticText
  22. from Components.Button import Button
  23. from Plugins.Plugin import PluginDescriptor
  24. from Screens.InfoBar import MoviePlayer
  25. from Screens.MessageBox import MessageBox
  26. from Tools import Notifications
  27. from Screens.LocationBox import LocationBox
  28. #from Screens.InputBox import InputBox
  29. from Screens.ChoiceBox import ChoiceBox
  30. from ChoiceBox import ChoiceBox2
  31. from Screens.VirtualKeyBoard import VirtualKeyBoard
  32. from Components.Input import Input
  33. from Screens.Screen import Screen
  34. from Tools.BoundFunction import boundFunction
  35. from Tools.Directories import resolveFilename, SCOPE_PLUGINS
  36. from Tools.LoadPixmap import LoadPixmap
  37. from twisted.web.client import downloadPage,defer,reactor
  38. from Components.Task import job_manager
  39. from Components.config import config, ConfigSubsection, ConfigText, ConfigInteger, ConfigLocations, ConfigDirectory, ConfigSet, ConfigYesNo, ConfigSelection, getConfigListEntry, ConfigSelectionNumber
  40. import ContentSources
  41. import util
  42. from sources.SourceBase import stream0
  43. from VideoDownload import downloadJob, HLSDownloadJob,VideoDownloadList
  44. #import enigma2_api
  45. TMPDIR = "/tmp/playstream/"
  46. e2 = None
  47. config.playstream = ConfigSubsection()
  48. config.playstream.locations = ConfigLocations(default=["/media/hdd/movie/"])
  49. config.playstream.download_dir = ConfigText(default="/media/hdd/movie/")
  50. def make_service(stream):
  51. url = stream["url"]
  52. headers = []
  53. if stream.has_key("headers"):
  54. for h in stream["headers"]:
  55. headers.append("%s=%s"%(h,stream["headers"][h]))
  56. if headers:
  57. headers ="&".join(headers)
  58. url = url+"#"+headers
  59. print "stream_url with headers=",headers
  60. url = url.encode("utf8")
  61. if "|" in url:
  62. url = url.replace("|","#")
  63. #url = url.replace(":","%3a")
  64. print "stream_url=",url
  65. service = eServiceReference(4097, 0, url)
  66. service.setName( stream["name"])
  67. return service
  68. #####################################################################################################################
  69. class PSSubs(Screen):
  70. def __init__(self, session):
  71. desktopWidth = getDesktop(0).size().width()
  72. desktopHeight = getDesktop(0).size().height()
  73. offset = 20
  74. screenWidth = desktopWidth - (2 * offset)
  75. widgetWidth = screenWidth / 2 - 5
  76. self.skin = """
  77. <screen position="%d,%d" size="%d,140" zPosition="2" backgroundColor="transparent" flags="wfNoBorder">
  78. <widget name="subtitle" position="0,0" size="%d,140" valign="center" halign="center" font="Regular;36" transparent="1" foregroundColor="white" shadowColor="#40101010" shadowOffset="2,2" />
  79. </screen>""" % (offset, desktopHeight-offset-140, screenWidth, screenWidth)
  80. self['subtitle'] = Label()
  81. Screen.__init__(self, session)
  82. #####################################################################################################################
  83. class PSPlayer(MoviePlayer):
  84. def __init__(self, session, streams):
  85. print "PSPlayer init: ",streams
  86. self.session = session
  87. self.streams = streams
  88. self.cur_stream = self.streams[0] # TODO
  89. self.selected = 0
  90. self.resume_pos = 0
  91. service = make_service(self.cur_stream)
  92. MoviePlayer.__init__(self, session, service)
  93. self.skinName = "MoviePlayer"
  94. self["actions"] = ActionMap(["MediaPlayerActions","MediaPlayerSeekActions","MoviePlayerActions","InfobarSeekActions","MovieSelectionActions","ColorActions"], {
  95. "stop":self.leavePlayer,
  96. "leavePlayer":self.leavePlayer,
  97. "audio":self.select_stream,
  98. "AudioSelection":self.select_stream,
  99. "green":self.select_stream,
  100. "subtitles":self.select_captions,
  101. "text":self.select_captions,
  102. "yellow_key":self.select_captions,
  103. "yellow":self.select_captions,
  104. "pauseServiceYellow":self.select_captions,
  105. "pause":self.select_captions,
  106. "showEventInfo":self.service_info,
  107. "info":self.service_info
  108. })
  109. self.stimer = eTimer()
  110. self.stimer.callback.append(self.update_subtitles)
  111. self.stimer_step = 500
  112. if self.cur_stream["subs"]:
  113. self.subs = self.cur_stream["subs"]
  114. self.cur_subs = 0 # TODO - no konfiguracijas
  115. self.svisible = True # TODO - no konfiguracija
  116. self.sind = 0
  117. self.get_subs_current()
  118. else:
  119. self.subs = []
  120. self.cur_subs = 0
  121. self.svisible = False
  122. self.subtitle_window = self.session.instantiateDialog (PSSubs)
  123. self.onLayoutFinish.append(self.start_subtitles_timer)
  124. def start_subtitles_timer(self):
  125. self.subtitle_window.show()
  126. print "start_subtitles_timer"
  127. self.stimer.start(self.stimer_step)
  128. def get_sub_pts(self,pts):
  129. sc = self.get_sub_ind(self.sind) # current subbtitle
  130. while True:
  131. if not sc:
  132. return "Error - no subs find" # TODO
  133. if pts > sc["end"]:
  134. self.sind += 1
  135. sc = self.get_sub_ind(self.sind)
  136. continue
  137. else:
  138. if pts <sc["begin"]:
  139. return " "
  140. else:
  141. txt = sc["text"] if sc["text"] else " "
  142. return txt
  143. def get_sub_ind(self,ind):
  144. subs_object = self.get_subs_current() # current subs object
  145. if subs_object:
  146. return subs_object.subs[ind] if ind<len(subs_object.subs) else None
  147. else:
  148. return None
  149. def get_subs_current(self):
  150. "Return current sub_object"
  151. if not "subs" in self.subs[self.cur_subs]:
  152. print "===== Captions to download", self.subs[self.cur_subs]["url"]
  153. subs_object = util.Captions(self.subs[self.cur_subs]["url"])
  154. print len(subs_object.subs), "items"
  155. self.subs[self.cur_subs]["subs"] = subs_object
  156. if not subs_object:
  157. return None
  158. else:
  159. return subs_object
  160. else:
  161. return self.subs[self.cur_subs]["subs"]
  162. def update_subtitles(self):
  163. if not self.shown and self.svisible:
  164. seek = self.getSeek()
  165. pos = seek.getPlayPosition()
  166. pts = pos[1]/90
  167. txt0 = "%d:%02d (%i)" % (pts/60/1000, (pts/1000)%60, pts)
  168. #print "Update_subtitles", txt0
  169. if not self.subtitle_window.shown:
  170. self.subtitle_window.show()
  171. if not self.subs:
  172. return
  173. txt = self.get_sub_pts(pts)
  174. #print "Show subtitle",txt
  175. #txt = txt0+": "+ txt
  176. self.subtitle_window["subtitle"].setText(txt)
  177. elif self.shown and self.svisible:
  178. if self.subtitle_window.shown:
  179. self.subtitle_window.hide()
  180. elif not self.svisible:
  181. if self.subtitle_window.shown:
  182. self.subtitle_window.hide()
  183. # struct SubtitleTrack
  184. # int type;
  185. # int pid;
  186. # int page_number;
  187. # int magazine_number;
  188. # std::string language_code;
  189. #selectedSubtitle = ???
  190. #self.enableSubtitle(selectedSubtitle)
  191. def start_subtitles_timer(self):
  192. self.stimer.start(self.stimer_step)
  193. def play_service(self,service):
  194. self.movieSelected(service)
  195. #def doShow(self):
  196. #self.svisible = False
  197. #InfoBarShowHide.doShow(self)
  198. #def doHide(self):
  199. #self.svisible = True
  200. #InfoBarShowHide.doHide(self)
  201. def service_info(self):
  202. print "########[MoviePlayer] service_info"
  203. text = "%s\n%s %s\n%s"%(self.cur_stream["name"],self.cur_stream["lang"],self.cur_stream["quality"],self.cur_stream["desc"])
  204. text = text.encode("utf8")
  205. #print text
  206. mtype = MessageBox.TYPE_INFO
  207. Notifications.AddPopup(text = text, type=mtype, timeout = 10)
  208. #Notifications.MessageBox(self.session, text=text, type=MessageBox.TYPE_INFO, timeout=10,
  209. #close_on_any_key=True,
  210. #default=True,
  211. #enable_input=True,
  212. #msgBoxID=None,
  213. #picon=False,
  214. #simple=False,
  215. #wizard=False,
  216. #list=None,
  217. #skin_name=None,
  218. #timeout_default=None)
  219. #return True
  220. def select_stream(self):
  221. print "########[MoviePlayer] select_stream"
  222. lst = []
  223. title = "Select stream"
  224. for i,s in enumerate(self.streams):
  225. lst.append(("[%s,%s] %s"%(s["lang"],s["quality"],s["name"]),i))
  226. self.session.openWithCallback(self.cb_select_stream, ChoiceBox2, title = title, list = lst,selection = self.selected)
  227. def cb_select_stream(self,answer):
  228. #print "item_menu_selected",answer
  229. if not answer:
  230. return
  231. self.selected = answer[1]
  232. service = make_service(self.streams[self.selected])
  233. self.resume_pos = self.getSeek().getPlayPosition()[1]
  234. self.play_service(service)
  235. def serviceStarted(self):
  236. print "serviceStarted"
  237. if not self.resume_pos:
  238. self.resume_pos = 0
  239. print "doSeek",self.resume_pos
  240. #self.doSeek(self.resume_pos)
  241. def select_captions(self):
  242. print "########[MoviePlayer] select_caption"
  243. lst = []
  244. title = "Select subtitles"
  245. for i,s in enumerate(self.subs):
  246. lst.append((("%s - %s"%(s["lang"],s["name"])).encode("utf8"),i))
  247. if self.svisible:
  248. selection = self.cur_subs
  249. else:
  250. selection = len(lst)
  251. lst.append(("No captions",-1))
  252. self.session.openWithCallback(self.cb_select_captions, ChoiceBox2, title = title, list = lst,selection = selection)
  253. def cb_select_captions(self,answer):
  254. #print "item_menu_selected",answer
  255. if not answer:
  256. return
  257. if answer[1] == -1:
  258. self.svisible = False
  259. else:
  260. self.cur_subs = answer[1]
  261. self.svisible = True
  262. def leavePlayer(self):
  263. self.close()
  264. #self.session.openWithCallback(self.leavePlayerConfirmed, MessageBox, _("Stop playing?"))
  265. def leavePlayerConfirmed(self, answer):
  266. if answer:
  267. self.close()
  268. def doEofInternal(self, playing):
  269. self.close()
  270. #def getPluginList(self):
  271. #from Components.PluginComponent import plugins
  272. #list = []
  273. #for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
  274. #if p.name != _("TV Play"):
  275. #list.append(((boundFunction(self.getPluginName, p.name),
  276. #boundFunction(self.runPlugin, p), lambda: True), None))
  277. #return list
  278. #def showMovies(self):
  279. #pass
  280. #####################################################################################################################
  281. class MainScreen(Screen):
  282. skin = """
  283. <screen position="center,center" size="1015,570" title="Play Stream">
  284. <eLabel position="5,0" size="1000,2" backgroundColor="#aaaaaa" />
  285. <widget name="title" position="10,2" size="1000,38" font="Regular;30" />
  286. <widget source="list" render="Listbox" position="10,55" size="580,470" \
  287. scrollbarMode="showOnDemand" >
  288. <convert type="TemplatedMultiContent" >
  289. {
  290. "template": [MultiContentEntryText(pos=(10, 1), size=(560, 30), \
  291. font=0, flags=RT_HALIGN_LEFT, text=0)],
  292. "fonts": [gFont("Regular", 20)],
  293. "itemHeight": 30
  294. }
  295. </convert>
  296. </widget>
  297. <widget name="pic" position="646,55" size="327,250" alphatest="on" />
  298. <widget name="cur" position="610,310" size="400,250" halign="center" font="Regular;20"/>
  299. <ePixmap name="red" position="100,530" zPosition="2" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
  300. <widget name="key_red" position="100,530" size="140,40" valign="center" halign="center" zPosition="4" foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
  301. <ePixmap name="green" position="240,530" zPosition="2" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
  302. <widget name="key_green" position="240,530" size="140,40" valign="center" halign="center" zPosition="4" foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
  303. <ePixmap name="yellow" position="380,530" zPosition="2" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
  304. <widget name="key_yellow" position="380,530" size="140,40" valign="center" halign="center" zPosition="4" foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
  305. <ePixmap name="menu" position="560,535" zPosition="2" size="140,40" pixmap="skin_default/buttons/key_menu.png" transparent="1" alphatest="on" />
  306. <widget name="key_menu" position="560,530" size="140,40" valign="center" halign="center" zPosition="4" backgroundColor="blue" foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
  307. </screen>"""
  308. def __init__(self, session):
  309. Screen.__init__(self, session)
  310. #self.setTitle2("Home")
  311. self.session = session
  312. self.e2 = None
  313. self["key_red"] = Button(_("Back"))
  314. self["key_green"] = Button(_("Select"))
  315. self["key_yellow"] = Button(_("Options"))
  316. self["key_menu"] = Button("Menu" )
  317. self["actions"] = ActionMap(["OkCancelActions", "ColorActions","MenuActions", "NumberActions"],
  318. {
  319. "cancel": self.Cancel,
  320. "ok": self.Ok,
  321. "green": self.Ok,
  322. "red": self.Back,
  323. "yellow": self.options_screen,
  324. "blue": self.download_list,
  325. "menu": self.item_menu,
  326. })
  327. self["list"] = List([])
  328. self["list"].onSelectionChanged.append(self.SelectionChanged)
  329. self["pic"] = Pixmap()
  330. self["cur"] = Label()
  331. self["title"] = Label()
  332. self.downloading = 0
  333. self.activeDownloads = 0
  334. if not os.path.exists(TMPDIR):
  335. os.mkdir(TMPDIR)
  336. self.onLayoutFinish.append(self.LayoutFinish)
  337. def LayoutFinish(self):
  338. self.cur_directory = os.path.dirname(os.path.realpath(__file__))
  339. self.defimage = LoadPixmap(os.path.join(self.cur_directory,"PlayStream.png"))
  340. #self.defimage = None
  341. #sc = AVSwitch().getFramebufferScale()
  342. #self.defimage0 = ePicLoad()
  343. #self.defimage0.PictureData.get().append(boundFunction(self.FinishDecodeDef, os.path.join(self.cur_directory,"PlayStream.png")))
  344. #self.defimage0.setPara((self["pic"].instance.size().width(),self["pic"].instance.size().height(),sc[0], sc[1], False, 0, "#00000000"))
  345. #self.defimage0.startDecode("default")
  346. self.activeDownloads = 0
  347. self.images = {}
  348. self.images_url = {}
  349. self.picloads = {}
  350. self.history = []
  351. reload(ContentSources)
  352. self.sources = ContentSources.ContentSources(os.path.join(self.cur_directory,"sources"))
  353. self.config = self.sources.plugins["config"]
  354. self.cur_menu = ("Home","config::home","","Sākums") #
  355. self.content = self.sources.get_content(self.cur_menu[1])
  356. #print self.content
  357. self["list"].setList(self.content)
  358. self["cur"].setText(self.content[0][3])
  359. self.setTitle2(self.cur_menu[0])
  360. self.ShowPic(self.content[0][2])
  361. def SelectionChanged(self):
  362. current = self["list"].getCurrent()
  363. print "[PlayStream] SelectionChanged: current=",current
  364. if not current: return
  365. self["cur"].setText(current[3]) if current[3] else self["cur"].setText("")
  366. if current[2]:
  367. self.ShowPic(current[2])
  368. else:
  369. self.ShowDefPic()
  370. def setTitle2(self,title):
  371. #print self.keys()
  372. self["title"].setText(title)
  373. def ShowDefPic(self):
  374. if self.defimage:
  375. self["pic"].instance.setPixmap(self.defimage)
  376. def ShowPic(self,image_url):
  377. if image_url == "default":
  378. self.ShowDefPic()
  379. return
  380. elif self.images.has_key(image_url):
  381. if self.images[image_url] in (-1,-2):
  382. return
  383. elif self.images[image_url] == -3:
  384. self.ShowDefPic()
  385. return
  386. else:
  387. self["pic"].instance.setPixmap(self.images[image_url])
  388. else:
  389. if image_url.startswith("http"):
  390. fname = image_url.replace(":","-").replace("/","_")
  391. image_path = os.path.join(TMPDIR, fname)
  392. self.download_image(image_path, image_url)
  393. else: # local file
  394. image_path = os.path.join(self.cur_directory,image_url)
  395. self.start_decode(image_path,image_url)
  396. def start_decode(self,image_path,image_url):
  397. self.images[image_url] = -2
  398. self.images_url[image_path] = image_url
  399. sc = AVSwitch().getFramebufferScale()
  400. if not self.picloads.has_key(image_path):
  401. self.picloads[image_path] = ePicLoad()
  402. self.picloads[image_path].PictureData.get().append(boundFunction(self.FinishDecode, image_path))
  403. self.picloads[image_path].setPara((self["pic"].instance.size().width(),
  404. self["pic"].instance.size().height(),
  405. sc[0], sc[1], True, 0, "#00000000"))
  406. #print image_path,image_url
  407. self.picloads[image_path].startDecode(image_path)
  408. def FinishDecode(self, image_path,picInfo = None):
  409. image_url = self.images_url[image_path]
  410. del self.images_url[image_path] #III
  411. self.images[image_url] = self.picloads[image_path].getData()
  412. self["pic"].instance.setPixmap(self.images[image_url])
  413. del self.picloads[image_path]
  414. if len(self.images)>30:
  415. del self.images[self.images.keys()[0]]
  416. # self.images.pop()
  417. #def FinishDecodeDef(self, image_path,picInfo = None):
  418. # self.defimage = self.defimage0.getData()
  419. # del self.defimage0
  420. # self["pic"].instance.setPixmap(self.defimage)
  421. def download_image(self,image_path,image_url):
  422. #print "Image download started",self.downloading,image_path,image_url
  423. self.downloading += 1
  424. self.images[image_url] = -1
  425. downloadPage(image_url, image_path).addCallback(boundFunction(self.downloadFinished, image_path,image_url)).addErrback(boundFunction(self.downloadFailed, image_path,image_url))
  426. def downloadFinished(self, image_path, image_url, result):
  427. self.downloading -= 1
  428. #print "[ Play] Image downloaded finished ",self.downloading,image_path, image_url,result
  429. self.start_decode(image_path,image_url)
  430. def downloadFailed(self, image_path, image_url,result):
  431. self.downloading -= 1
  432. print "[TV Play] Image downloaded failed ",self.downloading,image_path, image_url,result
  433. self.images[image_url] = -3
  434. def Ok(self):
  435. current = self["list"].getCurrent()
  436. self.current = current
  437. index = self["list"].getIndex()
  438. self.index = index
  439. print "[PlayStream] - menu selected ", current
  440. data = current[1].split("::")[1] if "::" in current[1] else current[1]
  441. if not data:
  442. return
  443. elif self.sources.is_video(current[1]):
  444. if self.sources.stream_type(current[1]):
  445. stream = stream0.copy()
  446. stream["url"] = current[1]
  447. stream["name"] = current[0]
  448. streams = [stream]
  449. else:
  450. try:
  451. streams = self.sources.get_streams(current[1])
  452. except Exception,e:
  453. print str(e)
  454. self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
  455. return
  456. if streams:
  457. #print streams
  458. for s in streams:
  459. if not s["name"]: s["name"] = current[0]
  460. if not s["img"]: s["img"] = current[2]
  461. if not s["desc"]: s["desc"] = current[3]
  462. try:
  463. self.session.open(PSPlayer, streams)
  464. except Exception as e:
  465. self.msg2("Error launching player - " + str(e))
  466. return
  467. else:
  468. self.msg("No stream found - %s"%(self.current[1]))
  469. return
  470. elif current[1] == "back":
  471. cur_menu_old = self.cur_menu
  472. self.cur_menu = self.history.pop()
  473. new_content = self.sources.get_content(self.cur_menu[1])
  474. try:
  475. index = zip(*new_content)[1].index(cur_menu_old[1])
  476. except:
  477. index = 0
  478. self.setTitle2(self.cur_menu[0])
  479. self.show_content(new_content,index)
  480. else:
  481. print "selected=",current
  482. if "{0}" in current[1]:
  483. self.session.openWithCallback(self.cb_input,VirtualKeyBoard, title="Enter value", text="")
  484. #a = raw_input("Enter value:")
  485. #a = "big bang"
  486. #current = (current[0],current[1].format(a),current[2],current[3])
  487. #self.get_content(current)
  488. else:
  489. self.get_content(current)
  490. def cb_input(self,value):
  491. if not value:
  492. return
  493. current = self.current
  494. current = (current[0],current[1].format(value),current[2],current[3])
  495. self.get_content(current)
  496. def get_content(self,current):
  497. self.history.append(self.cur_menu)
  498. self.cur_menu = current
  499. try:
  500. new_content = self.sources.get_content(self.cur_menu[1])
  501. except Exception,e:
  502. self.cur_menu = self.history.pop()
  503. self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
  504. return
  505. self.setTitle2(self.cur_menu[0])
  506. self.show_content(new_content)
  507. def Back(self):
  508. self["list"].setIndex(0)
  509. self.Ok()
  510. def show_content(self,content,index=0):
  511. self["list"].setList(content)
  512. self["list"].setIndex(index)
  513. self.SelectionChanged()
  514. def Cancel(self):
  515. #if os.path.exists(TMPDIR):
  516. #for name in os.listdir(TMPDIR):
  517. #os.remove(os.path.join(TMPDIR, name))
  518. #os.rmdir(TMPDIR)
  519. self.close()
  520. def item_menu(self):
  521. print "\n[PlayStream] options\n"
  522. self.current = self["list"].getCurrent()
  523. self.index = self["list"].getIndex()
  524. #self.session. open(MessageBox, "Item options - %s"%current[0] , MessageBox.TYPE_INFO)
  525. #args=[current,self.cur_menu]
  526. #self.session.open(ItemMenuScreen,current,index,self)
  527. lst = [
  528. ("Aditional information","info","Display additional information about item"),
  529. ("Add to bouquet","bouquet","Add current item to Enigma2 bouquet"),
  530. ("Add to favorites","favorites","Add current item to PlayStrem favorites"),
  531. ("Show active downloads","download_list","Show active downloads list"),
  532. ("Set download folder","download_folder","Set download folder")
  533. ]
  534. if self.sources.is_video(self.current[1]):
  535. lst.extend([
  536. ("Download video","download","Download video in background"),
  537. ])
  538. else:
  539. lst.extend([
  540. ("Download videos in folder","download","Download videos in folder (if any)"),
  541. ])
  542. if "config::" in self.cur_menu[1]:
  543. lst.extend([
  544. ("Rename item","rename","Rename list item"),
  545. ("Move item","move","Move list item"),
  546. ("Delete item","delete","Delete list item"),
  547. ("Add submenu","add_list","Add submenu before selected item"),
  548. ])
  549. title = self.current[0]
  550. self.session.openWithCallback(self.cb_item_menu, ChoiceBox2, title = title, list = lst) #TODO
  551. def cb_item_menu(self,answer):
  552. #print "item_menu_selected",answer
  553. if not answer:
  554. return
  555. if answer[1] == "info":
  556. self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO)
  557. pass # TODO parada papildus info
  558. elif answer[1] == "bouquet":
  559. #if not e2:
  560. #e2 = enigma2_api.DBServices()
  561. #print "load_buuquets - ",e2._load_bouquets()
  562. self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO)
  563. elif answer[1] == "favorites":
  564. lists = self.config.get_lists()
  565. lists2 = [(l,l) for l in lists]
  566. self.session.openWithCallback(self.cb_favorites, ChoiceBox2, title="Selected menu item will be added",list = lists2)
  567. elif answer[1] == 'download':
  568. current = self.current
  569. if not self.sources.is_video(current[1]):
  570. #self.msg("Can not download listst (yet) - %s"%(current[1]))
  571. n = 0
  572. for current2 in self.sources.get_content(current[1]):
  573. if self.sources.is_video(current2[1]):
  574. n += 1
  575. self.download_video(current2)
  576. if n>0:
  577. self.msg("%s videos download started"%n)
  578. else:
  579. self.msg("No videos to download")
  580. else:
  581. if self.download_video(current):
  582. self.msg("Video download started")
  583. elif answer[1] == 'download_list':
  584. self.download_list()
  585. elif answer[1] == 'download_folder':
  586. #downloadDir = "/media/hdd/movie" #config.plugins.playstream.downloadDir.value TODO
  587. self.session.openWithCallback(self.select_download_dir, LocationBox,"Select download folder","",config.playstream.download_dir.value,config.playstream.locations,False,"Select folder",None,True,True)
  588. elif answer[1] == "delete":
  589. lst = self.cur_menu[1].replace("config::","")
  590. #print lst
  591. self.config.del_item(lst,self.index)
  592. self.config.write_streams()
  593. txt = "'%s' deleted from favourite stream list '%s'"%(self.current[0],lst)
  594. self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=5)
  595. elif answer[1] == "rename":
  596. #name2 = "Renamed"
  597. self.session.openWithCallback(self.cb_rename,VirtualKeyBoard, title="Enter new item name", text=self.current[0])
  598. elif answer[1] == "add_list":
  599. self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO,timeout=5)
  600. pass #TODO
  601. return
  602. def select_download_dir(self, downloadDir, select=None):
  603. if not downloadDir:
  604. return
  605. print "Folder selected - %s"%downloadDir
  606. config.playstream.download_dir.setValue(downloadDir)
  607. config.playstream.download_dir.save()
  608. config.playstream.locations.save()
  609. config.save()
  610. def download_list(self):
  611. self.session.open(VideoDownloadList)
  612. def download_video(self,current):
  613. if self.sources.stream_type(current[1]):
  614. stream = stream0.copy()
  615. stream["url"] = current[1]
  616. stream["name"] = current[0]
  617. streams = [stream]
  618. else:
  619. try:
  620. streams = self.sources.get_streams(current[1])
  621. except Exception,e:
  622. print "Error - %s"%str(e)
  623. self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
  624. return
  625. if not streams:
  626. self.msg("No stream found to download - %s"%(self.current[1]))
  627. return
  628. for s in streams:
  629. if not s["name"]: s["name"] = current[0]
  630. if not s["img"]: s["img"] = current[2]
  631. if not s["desc"]: s["desc"] = current[3]
  632. if len(streams)>1:
  633. stream = streams[0] # TODO iespeja izvelēties strīmu, ja to ir vairāki
  634. else:
  635. stream = streams[0]
  636. return self.download_stream(stream)
  637. def download_stream(self,stream):
  638. print "download stream",stream
  639. #self.msg("Start downloading..")
  640. self.stream = stream
  641. stream_type = self.stream["type"] #self.sources.stream_type(stream["url"])
  642. if not stream_type: #
  643. print "Not supported stream type found to download - %s"%(self.current[1])
  644. self.msg("Not supported stream type found to download - %s"%(self.current[1]))
  645. return
  646. title = self.stream["name"].strip()
  647. url = self.stream["url"]
  648. stream_type = self.stream["type"] #self.sources.stream_type(stream["url"])
  649. downloadDir = config.playstream.download_dir.value
  650. if not os.path.exists(downloadDir):
  651. print 'Sorry, download directory "%s" not exist!\nPlease specify in the settings existing directory'%downloadDir
  652. self.msg0(_('Sorry, download directory "%s" not exist!\nPlease specify in the settings existing directory'%downloadDir))
  653. return
  654. fname0 = re.sub("[/\n\r\t,:]"," ",title)
  655. fname0 = re.sub("['""]","",fname0)
  656. fname = fname0 +".mp4"
  657. outputfile = os.path.join(downloadDir, fname)
  658. #print "Trying to download - ", current
  659. if self.stream["subs"]:
  660. suburl = self.stream["subs"][0]["url"]
  661. print "\n**Download subtitles %s - %s"%(title,suburl)
  662. subs = urllib2.urlopen(suburl).read()
  663. if subs:
  664. #fname0 = re.sub("[/\n\r\t,:]"," ",title)
  665. subext = ".srt"
  666. subfile = os.path.join(downloadDir,fname0+subext)
  667. if ".xml" in suburl:
  668. subs = util.ttaf2srt(subs)
  669. with open(subfile,"w") as f:
  670. f.write(subs)
  671. else:
  672. print "\n Error downloading subtitle %s"%suburl
  673. if os.path.exists(outputfile):
  674. self.msg( _('Sorry, this file already exists:\n%s') % outputfile)
  675. return False
  676. #os.remove(outputfile)
  677. if stream_type in ("http","https"):
  678. print "\n**Download %s - %s"%(title,url)
  679. #reload(downloadJob)
  680. job_manager.AddJob(downloadJob(url, outputfile, title[:20], self.video_download_stop))
  681. self.activeDownloads += 1
  682. #self.msg(_('Video download started!'))
  683. return True
  684. elif stream_type == "hls":
  685. #self.msg("HLS stream download not yet implemented!")
  686. print "\n**Download %s - %s"%(title,url)
  687. #reload(HLSDownloadJob)
  688. print "HLSDownload", url,outputfile
  689. job_manager.AddJob(HLSDownloadJob(url, outputfile, title[:20], self.video_download_stop))
  690. self.activeDownloads += 1
  691. #self.msg(_('Video download started!'))
  692. return True
  693. elif stream_type == "rstp":
  694. self.msg("RSTP stream download not yet implemented!")
  695. return False
  696. else:
  697. self.msg("Unkown stream type!")
  698. return False
  699. def cb_rename(self,value):
  700. if not value:
  701. return
  702. lst = self.cur_menu[1].replace("config::","")
  703. pos = self.index
  704. print value
  705. item2 = list(self.current)
  706. item2[0]=value
  707. item2 = tuple(item2)
  708. self.config.replace_item(lst,item2,pos)
  709. self.config.write_streams()
  710. txt = "'%s' renamed to '%s'"%(self.current[0],value)
  711. self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=5)
  712. def cb_favorites(self,answer):
  713. print "cb_favorites",answer,self.current
  714. if not answer:
  715. return
  716. value = answer[1]
  717. self.config.add_item(value,self.current)
  718. self.config.write_streams()
  719. txt = "'%s' added to favourite stream list '%s'"%(self.current[0],value)
  720. self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=3)
  721. #self.session.openWithCallback(self.callMyMsg, MessageBox, _("Do you want to exit the plugin?"), MessageBox.TYPE_INFO)
  722. def options_screen(self):
  723. source = self.cur_menu[1].split("::")[0]
  724. options = self.sources.options_read(source)
  725. print source
  726. if not options:
  727. self.session. open(MessageBox, "No options available for source %s (%s)"%(self.cur_menu[0],source) , MessageBox.TYPE_INFO)
  728. else:
  729. self.session.open(OptionsScreen,self)
  730. def video_download_stop(self,title):
  731. #self.activeDownloads -= 1
  732. #self.msg("Download '%s'finished!"%title)
  733. print "video_download_stop ", title
  734. def msg2(self,msg,timeout=10,mtype = None):
  735. mtype=mtype if mtype else MessageBox.TYPE_INFO
  736. Notifications.AddPopup(text = msg, type=mtype, timeout=timeout)
  737. def msg(self,msg,timeout=10):
  738. self.session.open(MessageBox, msg, MessageBox.TYPE_INFO, timeout)
  739. ##########################################################################
  740. from Components.config import config, ConfigSubsection, ConfigYesNo,\
  741. getConfigListEntry, ConfigSelection, ConfigNumber, ConfigDirectory,ConfigText, ConfigSubDict
  742. from Components.ConfigList import ConfigListScreen
  743. #from Screens.LocationBox import LocationBox
  744. config.plugins.playstream = ConfigSubDict()
  745. class OptionsScreen(ConfigListScreen,Screen):
  746. skin = """
  747. <screen position="center,center" size="560,400" >
  748. <ePixmap name="red" position="0,0" zPosition="2" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
  749. <widget name="key_red" position="0,0" size="140,40" valign="center" halign="center" zPosition="4" foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
  750. <ePixmap name="green" position="140,0" zPosition="2" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
  751. <widget name="key_green" position="140,0" size="140,40" valign="center" halign="center" zPosition="4" foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
  752. <ePixmap name="yellow" position="280,0" zPosition="2" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
  753. <widget name="key_yellow" position="280,0" size="140,40" valign="center" halign="center" zPosition="4" foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
  754. <ePixmap name="blue" position="420,0" zPosition="2" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
  755. <widget name="key_blue" position="420,0" size="140,40" valign="center" halign="center" zPosition="4" foregroundColor="white" font="Regular;20" transparent="1" shadowColor="background" shadowOffset="-2,-2" />
  756. <widget name="config" position="10,40" size="540,340" scrollbarMode="showOnDemand" />
  757. </screen>"""
  758. def __init__(self, session,*args):
  759. self.session = session
  760. Screen.__init__(self, session)
  761. self.main = args[0]
  762. self.setTitle(self.main.cur_menu[0]+" Options")
  763. self.source = self.main.cur_menu[1].split("::")[0]
  764. self.cfg = config.plugins.playstream
  765. self.list = []
  766. self.options = self.main.sources.options_read(self.source)
  767. if not self.options:
  768. #self.session. open(MessageBox, "No options available for source %s (%s)"%(self.main.cur_menu[0],self.source) , MessageBox.TYPE_INFO)
  769. self.close(False,self.session)
  770. for k in self.options:
  771. self.cfg[k]=ConfigText(default=self.options[k],fixed_size=False)
  772. self.list.append(getConfigListEntry(k, self.cfg[k]))
  773. ConfigListScreen.__init__(self, self.list, session = self.session)
  774. self["key_red"] = Button(_("Cancel"))
  775. self["key_green"] = Button("Save")
  776. self["key_yellow"] = Button("")
  777. self["key_blue"] = Button("")
  778. self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
  779. {
  780. "red": self.cancel,
  781. "green": self.save,
  782. "save": self.save,
  783. "cancel": self.cancel,
  784. "ok": self.ok,
  785. }, -2)
  786. def getCurrentEntry(self):
  787. return self["config"].getCurrent()[0]
  788. def getCurrentValue(self):
  789. return str(self["config"].getCurrent()[1].getText())
  790. def ok(self):
  791. self.save()
  792. #if self["config"].getCurrent()[1] == config.plugins.getpicons.folder:
  793. #folder = config.plugins.getpicons.folder.value
  794. #self.session.openWithCallback(self.change_dir, LocationBox,"Select Folder")
  795. #else:
  796. #def change_dir(self, folder, select=None):
  797. #if folder:
  798. ##print "change_dir to %s"%folder
  799. #config.plugins.getpicons.folder.value = folder
  800. def save(self):
  801. print "saving"
  802. #self.saveAll()
  803. for k in self.options.keys():
  804. self.options[k]=self.cfg[k].value
  805. print "%s=%s"%(k,self.cfg[k].value)
  806. self.main.sources.options_write(self.source,self.options)
  807. self.close(True,self.session)
  808. def cancel(self):
  809. print "cancel"
  810. self.close(False,self.session)