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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304
  1. #!/usr/bin/env python
  2. # coding=utf8
  3. # This file is part of PlayStream - enigma2 plughhhhin to play video streams from various sources
  4. # Copyright (c) 2016 ivars777 (ivars777@gmail.com)
  5. # Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
  6. # Used fragments of code from enigma2-plugin-tv3play by Taapat (https://github.com/Taapat/enigma2-plugin-tv3play)
  7. #
  8. __version__ = "0.8r"
  9. __id__ = "playstream"
  10. __title__ = "PlayStream"
  11. __author__ = "ivars777@gmail.com"
  12. __desc__ = "Play online video streams from various sources"
  13. import os,time,sys,os,os.path
  14. import datetime,re, traceback
  15. import urllib2
  16. from Plugins.Plugin import PluginDescriptor
  17. from enigma import ePicLoad, eServiceReference, eTimer, getDesktop
  18. from Screens.Screen import Screen
  19. from Screens.InfoBarGenerics import InfoBarShowHide
  20. from Screens.InfoBar import MoviePlayer
  21. from Screens.MessageBox import MessageBox
  22. from Screens.LocationBox import LocationBox
  23. from Screens.ChoiceBox import ChoiceBox
  24. from Screens.VirtualKeyBoard import VirtualKeyBoard
  25. from Screens.Console import Console
  26. from Screens.Standby import TryQuitMainloop
  27. #from Screens.InputBox import InputBox
  28. from Components.AVSwitch import AVSwitch
  29. from Components.ActionMap import ActionMap, HelpableActionMap
  30. from Components.Label import Label
  31. from Components.ScrollLabel import ScrollLabel
  32. from Components.Input import Input
  33. from Components.MenuList import MenuList
  34. from Components.Pixmap import Pixmap
  35. from Components.Sources.List import List
  36. from Components.Sources.StaticText import StaticText
  37. from Components.Button import Button
  38. from Components.Task import job_manager
  39. from Components.config import config, ConfigSubsection, ConfigText, ConfigInteger, ConfigLocations, ConfigDirectory, ConfigSet, ConfigYesNo, ConfigSelection, getConfigListEntry, ConfigSelectionNumber, ConfigNumber
  40. from Components.config import config, ConfigSubsection, ConfigYesNo, getConfigListEntry, ConfigSelection, ConfigNumber, ConfigDirectory,ConfigText, ConfigSubDict
  41. from Components.ConfigList import ConfigListScreen
  42. #from Screens.LocationBox import LocationBox
  43. from Tools import Notifications
  44. from Tools.BoundFunction import boundFunction
  45. from Tools.Directories import resolveFilename, SCOPE_PLUGINS
  46. from Tools.LoadPixmap import LoadPixmap
  47. from skin import loadSkin
  48. from twisted.web.client import downloadPage,defer,reactor
  49. try:
  50. # available since twisted 14.0
  51. from twisted.internet._sslverify import ClientTLSOptions
  52. except ImportError:
  53. ClientTLSOptions = None
  54. from twisted.internet.ssl import ClientContextFactory
  55. from plugin_locale import _
  56. from content import ContentSources
  57. from content import util
  58. from VideoDownload import DownloadJob, HLSDownloadJob,VideoDownloadList
  59. from content.Downloader import get_header, get_ext
  60. #import enigma2_api
  61. e2 = None
  62. cur_directory = os.path.dirname(os.path.realpath(__file__))
  63. loadSkin(os.path.join(cur_directory, "skin.xml"))
  64. #####################################################################################################################
  65. config.plugins.playstream = ConfigSubsection()
  66. config.plugins.playstream.locations = ConfigLocations(default=["/media/hdd/movie/"])
  67. config.plugins.playstream.download_dir = ConfigDirectory(default="/media/hdd/movie/")
  68. config.plugins.playstream.tmp_dir = ConfigDirectory(default="/tmp/playstream/")
  69. config.plugins.playstream.streamproxy_start = ConfigYesNo(default = True)
  70. config.plugins.playstream.overwrite_download = ConfigYesNo(default = False)
  71. #config.plugins.playstream.size = ConfigSelection({"400x240":"400x240","220x132":"220x132","100x60":"100x60"}, default="220x132")
  72. config.plugins.playstream.clear_tmp = ConfigYesNo(default = True)
  73. config.plugins.playstream.check_update = ConfigYesNo(default = True)
  74. config.plugins.playstream.streams_file = ConfigText(default="streams.cfg")
  75. config.plugins.playstream.streams_file_remote = ConfigText(default="ftp://user:password@host/hdd/streams.cfg")
  76. config.plugins.playstream.use_streams_file_remote = ConfigYesNo(default = False)
  77. class CustomContextFactory(ClientContextFactory):
  78. def __init__(self, hostname = None):
  79. self.hostname = hostname
  80. def getContext(self):
  81. ctx = self._contextFactory(self.method)
  82. if self.hostname and ClientTLSOptions:
  83. ClientTLSOptions(self.hostname, ctx)
  84. return ctx
  85. #####################################################################################################################
  86. class MainScreen(Screen):
  87. def __init__(self, session):
  88. Screen.__init__(self, session)
  89. self.skinName = ["PSMain"]
  90. self.session = session
  91. self.e2 = None
  92. self["key_red"] = Button(_("Exit"))
  93. self["key_green"] = Button(_("Select"))
  94. self["key_yellow"] = Button(_("Options"))
  95. self["key_blue"] = Button(_("Config"))
  96. self["key_menu"] = Button("Menu")
  97. self["key_exit"] = Button("Back")
  98. self["actions"] = ActionMap(["OkCancelActions", "ColorActions","MenuActions", "NumberActions",
  99. "WizardActions","ButtonSetupActions"], {
  100. #"cancel": self.Cancel,
  101. "ok": self.ok,
  102. "green": self.ok,
  103. "red": self.cancel,
  104. "yellow": self.options_screen,
  105. "blue": self.config_screen,
  106. "menu": self.item_menu,
  107. "cancel": self.back,
  108. "info": self.show_info,
  109. "pageup": self.page_up,
  110. "channelup":self.page_up,
  111. "pagedown": self.page_down,
  112. "channeldown":self.page_down,
  113. })
  114. self["list"] = List([])
  115. self["list"].onSelectionChanged.append(self.selection_changed)
  116. self["pic"] = Pixmap()
  117. self["cur"] = ScrollLabel()
  118. self["title"] = Label()
  119. self.downloading = 0
  120. self.active_downloads = 0
  121. if not os.path.exists(config.plugins.playstream.tmp_dir.value):
  122. os.mkdir(config.plugins.playstream.tmp_dir.value)
  123. self.onLayoutFinish.append(self.layout_finished)
  124. def layout_finished(self):
  125. self.defimage = LoadPixmap(os.path.join(cur_directory,"PlayStream.png"))
  126. #self.defimage = None
  127. #sc = AVSwitch().getFramebufferScale()
  128. #self.defimage0 = ePicLoad()
  129. #self.defimage0.PictureData.get().append(boundFunction(self.FinishDecodeDef, os.path.join(cur_directory,"PlayStream.png")))
  130. #self.defimage0.setPara((self["pic"].instance.size().width(),self["pic"].instance.size().height(),sc[0], sc[1], False, 0, "#00000000"))
  131. #self.defimage0.startDecode("default")
  132. self.active_downloads = 0
  133. self.images = {}
  134. self.images_url = {}
  135. self.picloads = {}
  136. self.history = []
  137. reload(ContentSources)
  138. sources_directory = os.path.join(cur_directory, "content", "sources")
  139. print "**** use_streams_file_remote=", config.plugins.playstream.use_streams_file_remote.value
  140. print "**** streams_file_remote=", config.plugins.playstream.streams_file_remote.value
  141. print "**** streams_file=", config.plugins.playstream.streams_file.value
  142. print "**** sources_directory=", sources_directory
  143. if config.plugins.playstream.use_streams_file_remote.value:
  144. try:
  145. self.sources = ContentSources.ContentSources(sources_directory, config.plugins.playstream.streams_file_remote.value)
  146. print "Remote stream file opened"
  147. except Exception as e:
  148. try:
  149. self.sources = ContentSources.ContentSources(sources_directory, config.plugins.playstream.streams_file.value)
  150. print "Streams_file fallback"
  151. self.msg2("Remote streams file is not available, fallback to local")
  152. except Exception as e:
  153. self.msg2(e.message)
  154. else:
  155. try:
  156. self.sources = ContentSources.ContentSources(sources_directory, config.plugins.playstream.streams_file.value)
  157. print "Local stream file opened"
  158. except Exception as e:
  159. self.msg2(e.message)
  160. self.cancel()
  161. if "config" not in self.sources.plugins:
  162. self.msg2("No streams config file")
  163. self.cancel()
  164. self.config = self.sources.plugins["config"]
  165. self.cur_menu = ("Home","config::home","","Sākums") #
  166. self.content = self.sources.get_content(self.cur_menu[1])
  167. #print self.content
  168. self["list"].setList(self.content)
  169. self["cur"].setText(self.content[0][3])
  170. self.setTitle2(self.cur_menu[0] + " (" + self.cur_menu[1].split("::")[0]+")")
  171. self.show_picture(self.content[0][2])
  172. reactor.callLater(1,self.check_update)
  173. def check_update(self):
  174. #print "Check PlayStream for update"
  175. if config.plugins.playstream.check_update:
  176. self.cver = util.check_version("enigma2-plugin-extensions-playstream")
  177. if self.cver and self.cver>__version__:
  178. txt = "New version of plugin available for update - %s\nCurrent version - %s\nDo you want to upgrade?"%(self.cver,__version__)
  179. mbox = self.session.openWithCallback(self.update, MessageBox, txt, MessageBox.TYPE_YESNO)
  180. mbox.setTitle("Upgrade plugin?")
  181. def update(self,answer):
  182. if answer:
  183. plugin_update(self.session)
  184. def page_down(self):
  185. self["cur"].pageDown()
  186. def page_up(self):
  187. self["cur"].pageUp()
  188. def selection_changed(self):
  189. current = self["list"].getCurrent()
  190. print "[PlayStream] SelectionChanged: current=",current[1]
  191. if not current: return
  192. self["cur"].setText(current[3]) if current[3] else self["cur"].setText("")
  193. if current[2]:
  194. self.show_picture(current[2])
  195. else:
  196. self.show_default_picture()
  197. def setTitle2(self,title):
  198. #print self.keys()
  199. self["title"].setText(title)
  200. def show_default_picture(self):
  201. if self.defimage:
  202. self["pic"].instance.setPixmap(self.defimage)
  203. def show_picture(self, image_url):
  204. if image_url == "default":
  205. self.show_default_picture()
  206. return
  207. elif self.images.has_key(image_url):
  208. if self.images[image_url] in (-1,-2):
  209. return
  210. elif self.images[image_url] == -3:
  211. self.show_default_picture()
  212. return
  213. else:
  214. self["pic"].instance.setPixmap(self.images[image_url])
  215. else:
  216. if image_url.startswith("http"):
  217. fname = image_url.replace(":","-").replace("/","_")
  218. image_path = os.path.join(config.plugins.playstream.tmp_dir.value, fname)
  219. self.download_image(image_path, image_url)
  220. else: # local file
  221. image_path = os.path.join(cur_directory, "picons", image_url)
  222. self.start_decode(image_path,image_url)
  223. def start_decode(self,image_path,image_url):
  224. self.images[image_url] = -2
  225. self.images_url[image_path] = image_url
  226. sc = AVSwitch().getFramebufferScale()
  227. if not self.picloads.has_key(image_path):
  228. self.picloads[image_path] = ePicLoad()
  229. try:
  230. self.picloads[image_path].PictureData.get().append(boundFunction(self.decode_finished, image_path))
  231. except:
  232. self.picloads[image_path].PictureData.connect(boundFunction(self.decode_finished, image_path))
  233. self.picloads[image_path].setPara((self["pic"].instance.size().width(),
  234. self["pic"].instance.size().height(),
  235. sc[0], sc[1], True, 0, "#00000000"))
  236. print image_path,image_url
  237. self.picloads[image_path].startDecode(image_path)
  238. def decode_finished(self, image_path, picInfo = None):
  239. image_url = self.images_url[image_path]
  240. del self.images_url[image_path] #III
  241. self.images[image_url] = self.picloads[image_path].getData()
  242. self["pic"].instance.setPixmap(self.images[image_url])
  243. del self.picloads[image_path]
  244. if len(self.images)>30:
  245. del self.images[self.images.keys()[0]]
  246. # self.images.pop()
  247. #def FinishDecodeDef(self, image_path,picInfo = None):
  248. # self.defimage = self.defimage0.getData()
  249. # del self.defimage0
  250. # self["pic"].instance.setPixmap(self.defimage)
  251. def download_image(self,image_path,image_url):
  252. #print "Image download started",self.downloading,image_path,image_url
  253. self.downloading += 1
  254. self.images[image_url] = -1
  255. downloadPage(image_url, image_path, CustomContextFactory(image_url.split("/")[2])).addCallback(boundFunction(self.download_image_finished, image_path, image_url)).addErrback(boundFunction(self.download_image_failed, image_path, image_url))
  256. def download_image_finished(self, image_path, image_url, result):
  257. self.downloading -= 1
  258. #print "[ Play] Image downloaded finished ",self.downloading,image_path, image_url,result
  259. self.start_decode(image_path,image_url)
  260. def download_image_failed(self, image_path, image_url, result):
  261. self.downloading -= 1
  262. print "[TV Play] Image downloaded failed ",self.downloading,image_path, image_url,result
  263. self.images[image_url] = -3
  264. def ok(self):
  265. current = self["list"].getCurrent()
  266. self.current = current
  267. index = self["list"].getIndex()
  268. self.index = index
  269. print "[PlayStream] - menu selected ", current
  270. data = current[1].split("::")[1] if "::" in current[1] else current[1]
  271. if not data:
  272. return
  273. if self.sources.is_video(current[1]):
  274. if ContentSources.stream_type(current[1]):
  275. stream = util.item()
  276. stream["url"] = current[1]
  277. stream["name"] = current[0]
  278. streams = [stream]
  279. else:
  280. try:
  281. streams = self.sources.get_streams(current[1])
  282. except Exception,e:
  283. print str(e)
  284. traceback.print_exc()
  285. self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
  286. return
  287. if streams:
  288. #print streams
  289. for s in streams:
  290. if not s["name"]: s["name"] = current[0]
  291. if not s["img"]: s["img"] = current[2]
  292. if not s["desc"]: s["desc"] = current[3]
  293. try:
  294. self.session.open(PSPlayer, streams)
  295. except Exception as e:
  296. traceback.print_exc()
  297. self.msg("Error launching player - " + str(e))
  298. return
  299. else:
  300. self.msg("No stream found - %s"%(self.current[1]))
  301. return
  302. elif current[1] == "back":
  303. cur_menu_old = self.cur_menu
  304. self.cur_menu = self.history.pop()
  305. self.update_streams_file()
  306. new_content = self.sources.get_content(self.cur_menu[1])
  307. try:
  308. index = zip(*new_content)[1].index(cur_menu_old[1])
  309. except:
  310. index = 0
  311. self.setTitle2(self.cur_menu[0]+ " (" + self.cur_menu[1].split("::")[0]+")")
  312. self.show_content(new_content,index)
  313. else:
  314. print "selected=",current
  315. if "{0}" in current[1]:
  316. self.session.openWithCallback(self.cb_input,VirtualKeyBoard, title="Enter value", text="")
  317. #a = raw_input("Enter value:")
  318. #a = "big bang"
  319. #current = (current[0],current[1].format(a),current[2],current[3])
  320. #self.get_content(current)
  321. else:
  322. self.history.append(self.cur_menu)
  323. self.update_content(current)
  324. def cb_input(self,value):
  325. if not value:
  326. return
  327. current = self.current
  328. current = (current[0],current[1].format(value),current[2],current[3])
  329. self.history.append(self.cur_menu)
  330. self.update_content(current)
  331. def update_content(self,current=None):
  332. if current:
  333. self.cur_menu = current
  334. self.update_streams_file()
  335. try:
  336. new_content = self.sources.get_content(self.cur_menu[1])
  337. except Exception,e:
  338. self.cur_menu = self.history.pop()
  339. traceback.print_exc()
  340. self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
  341. return
  342. self.setTitle2(self.cur_menu[0] + " (" + self.cur_menu[1].split("::")[0]+")")
  343. self.show_content(new_content)
  344. def show_content(self,content,index=0):
  345. self["list"].setList(content)
  346. self["list"].setIndex(index)
  347. self.selection_changed()
  348. def show_info(self):
  349. self.current = self["list"].getCurrent()
  350. data = self.current[1]
  351. source = data.split("::")[0]
  352. if self.sources.is_video(data) and "get_info" in dir(self.sources.plugins[source]):
  353. #self.sources.plugins
  354. try:
  355. nfo = self.sources.get_info(self.current[1])
  356. except Exception, e:
  357. print str(e)
  358. traceback.print_exc()
  359. self.session.open(MessageBox, "Error - %s" % str(e), MessageBox.TYPE_INFO)
  360. return
  361. else:
  362. nfo = {"movie": {"title": self.current[0], "thumb": self.current[2], "plot": self.current[3]}}
  363. self.session.open(StreamInfo, nfo)
  364. def back(self):
  365. if self.history:
  366. self["list"].setIndex(0)
  367. self.ok()
  368. else:
  369. self.cancel()
  370. def update_streams_file(self):
  371. if config.plugins.playstream.use_streams_file_remote.value:
  372. self.config.set_streams_file(config.plugins.playstream.streams_file_remote.value)
  373. else:
  374. self.config.set_streams_file(config.plugins.playstream.streams_file.value)
  375. def cancel(self):
  376. print "Exiting PlayStream"
  377. if config.plugins.playstream.clear_tmp.value and os.path.exists(config.plugins.playstream.tmp_dir.value):
  378. for name in os.listdir(config.plugins.playstream.tmp_dir.value):
  379. #print "remove "+os.path.join(config.plugins.playstream.tmp_dir.value, name)
  380. os.remove(os.path.join(config.plugins.playstream.tmp_dir.value, name))
  381. #os.rmdir(config.plugins.playstream.tmp_dir.value)
  382. self.close()
  383. def item_menu(self):
  384. print "\n[PlayStream] options\n"
  385. self.current = self["list"].getCurrent()
  386. self.index = self["list"].getIndex()
  387. #self.session. open(MessageBox, "Item options - %s"%current[0] , MessageBox.TYPE_INFO)
  388. #args=[current,self.cur_menu]
  389. #self.session.open(ItemMenuScreen,current,index,self)
  390. lst = [
  391. ("Aditional information","info","Display additional information about item"),
  392. ("Add to bouquet","bouquet","Add current item to Enigma2 bouquet"),
  393. ("Add to favorites","favorites","Add current item to PlayStrem favorites"),
  394. ]
  395. if "config::" in self.cur_menu[1]:
  396. lst.extend([
  397. ("Rename item","rename","Rename list item"),
  398. ("Move item","move","Move list item"),
  399. ("Delete item","delete","Delete list item"),
  400. ("Add submenu","add_list","Add submenu before selected item"),
  401. ])
  402. lst.extend([
  403. ("Show active downloads","download_list","Show active downloads list"),
  404. ("Set download folder","download_folder","Set download folder")
  405. ])
  406. if self.sources.is_video(self.current[1]):
  407. lst.extend([
  408. ("Download video to default folder","download","Download video to default folder"),
  409. ("Download video, ask folder","download2","Download video, ask download folder"),
  410. ("Download video to subfolder, ask parent folder","download3","Download video to subfolder, ask download folder"),
  411. ])
  412. else:
  413. lst.extend([
  414. ("Download list to default folder","download","Download videos in list (if any)"),
  415. ("Download list, ask folder","download2","Download videos in list (if any), ask download folder"),
  416. ])
  417. title = self.current[0]
  418. self.session.openWithCallback(self.cb_item_menu, ChoiceBox, title = title, list = lst) #TODO
  419. def cb_item_menu(self,answer):
  420. #print "item_menu_selected",answer
  421. if not answer:
  422. return
  423. self.answer = answer[1]
  424. if answer[1] == "info":
  425. #self.show_nfo()
  426. #self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO)
  427. self.show_info()
  428. elif answer[1] == "bouquet":
  429. #if not e2:
  430. #e2 = enigma2_api.DBServices()
  431. #print "load_buuquets - ",e2._load_bouquets()
  432. self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO)
  433. elif answer[1] == "favorites":
  434. lists = self.config.get_lists()
  435. lists2 = [(self.config.get_title(l),l) for l in lists]
  436. #print lists2
  437. self.session.openWithCallback(self.cb_favorites, ChoiceBox, title="Selected menu item will be added",list = lists2)
  438. elif answer[1] == "delete":
  439. lst = self.cur_menu[1].replace("config::","")
  440. #print lst
  441. self.config.del_item(lst,self.index)
  442. self.config.write_streams()
  443. self.update_content()
  444. txt = "'%s' deleted from favourite stream list '%s'"%(self.current[0],lst)
  445. self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=5)
  446. elif answer[1] == "move":
  447. lst = self.cur_menu[1].replace("config::","")
  448. items = self.config.get_list_items(lst)
  449. self.session.openWithCallback(self.cb_move, ChoiceBox, title="Move before selected item",list = items)
  450. #self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO)
  451. #self.config.write_streams()
  452. #txt = "'%s' deleted from favourite stream list '%s'"%(self.current[0],lst)
  453. #self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=5)
  454. elif answer[1] == "rename":
  455. #name2 = "Renamed"
  456. self.session.openWithCallback(self.cb_rename,VirtualKeyBoard, title="Enter new item name", text=self.current[0])
  457. elif answer[1] == "add_list":
  458. self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO,timeout=5)
  459. pass #TODO
  460. elif answer[1] in ("download2","download3"): # ask download folder before
  461. self.session.openWithCallback(self.download_prepare, LocationBox,"Select download folder","",config.plugins.playstream.download_dir.value,config.plugins.playstream.locations,False,"Select folder",None,True,True)
  462. elif answer[1] == 'download':
  463. self.download_prepare(config.plugins.playstream.download_dir.value)
  464. elif answer[1] == 'download_list':
  465. self.download_list()
  466. elif answer[1] == 'download_folder':
  467. #downloadDir = "/media/hdd/movie" #config.plugins.playstream.downloadDir.value TODO
  468. self.session.openWithCallback(self.cb_download_dir, LocationBox,"Select download folder","",config.plugins.playstream.download_dir.value,config.plugins.playstream.locations,False,"Select folder",None,True,True)
  469. return
  470. def download_prepare(self, download_dir=""):
  471. current = self.current
  472. if not download_dir:
  473. return
  474. #download_dir = config.plugins.playstream.download_dir.value
  475. title = re.sub("\[.+?\]","", util.make_fname(current[0])).strip()
  476. download_dir2 = os.path.join(download_dir, title)
  477. if not self.sources.is_video(current[1]):
  478. #self.msg("Can not download listst (yet) - %s"%(current[1]))
  479. self.update_streams_file()
  480. n = 0
  481. for current2 in self.sources.get_content(current[1]):
  482. if self.sources.is_video(current2[1]):
  483. n += 1
  484. self.download_video(current2,download_dir2)
  485. if n>0:
  486. self.msg("%s videos download started to %s"%(n,download_dir2))
  487. else:
  488. self.msg("No videos to download")
  489. else:
  490. if self.answer == "download":
  491. download_dir2 = download_dir
  492. if self.download_video(current,download_dir2):
  493. self.msg("Video download started to %s"%download_dir2)
  494. def cb_download_dir(self, downloadDir, select=None):
  495. if not downloadDir:
  496. return
  497. print "Folder selected - %s"%downloadDir
  498. config.plugins.playstream.download_dir.setValue(downloadDir)
  499. config.plugins.playstream.download_dir.save()
  500. config.plugins.playstream.locations.save()
  501. config.save()
  502. def download_list(self):
  503. self.session.open(VideoDownloadList)
  504. def download_video(self,current, download_dir=""):
  505. if ContentSources.stream_type(current[1]):
  506. stream = util.item()
  507. stream["url"] = current[1]
  508. stream["name"] = current[0]
  509. streams = [stream]
  510. else:
  511. try:
  512. streams = self.sources.get_streams(current[1])
  513. except Exception,e:
  514. print "Error - %s"%str(e)
  515. traceback.print_exc()
  516. self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
  517. return
  518. if not streams:
  519. self.msg("No stream found to download - %s"%(self.current[1]))
  520. return
  521. for s in streams:
  522. if not s["name"]: s["name"] = current[0]
  523. if not s["img"]: s["img"] = current[2]
  524. if not s["desc"]: s["desc"] = current[3]
  525. if len(streams)>1:
  526. stream = streams[0] # TODO iespeja izvelēties strīmu, ja to ir vairāki
  527. else:
  528. stream = streams[0]
  529. stream = util.stream_change(stream)
  530. return self.download_stream(stream, download_dir)
  531. def download_stream(self,stream,download_dir=""):
  532. print "download stream",stream
  533. #self.msg("Start downloading..")
  534. self.stream = stream
  535. url = stream["url"]
  536. headers = stream["headers"] if "headers" in stream else {}
  537. stream_type = ContentSources.stream_type(url) #self.sources.stream_type(stream["url"])
  538. if not stream_type: #
  539. print "Not supported stream type found to download - %s"%(self.current[1])
  540. self.msg("Not supported stream type found to download - %s"%(self.current[1]))
  541. return
  542. title = self.stream["name"].strip()
  543. downloadDir = config.plugins.playstream.download_dir.value if not download_dir else download_dir
  544. if not os.path.exists(downloadDir):
  545. try:
  546. os.mkdir(downloadDir)
  547. except Exception as e:
  548. traceback.print_exc()
  549. print 'Error creating download directory "%s"!\nPlease specify in the settings existing directory\n%s'%(downloadDir,str(e))
  550. self.msg('Error creating download directory "%s"!\nPlease specify in the settings existing directory\n%s'%(downloadDir,str(e)))
  551. return
  552. fname0 = util.make_fname(title)
  553. #print "Trying to download - ", current
  554. if self.stream["img"]:
  555. try:
  556. thumb = urllib2.urlopen(stream["img"]).read()
  557. except:
  558. thumb = ""
  559. if thumb:
  560. imgext = ".jpg"
  561. m = re.search("(\.\w{3})$",stream["img"])
  562. if m:
  563. imgext = m.group(1)
  564. imgfile = os.path.join(downloadDir,fname0+imgext)
  565. with open(imgfile,"wb") as f:
  566. f.write(thumb)
  567. if self.stream["subs"]:
  568. suburl = self.stream["subs"][0]["url"]
  569. print "\n**Download subtitles %s - %s"%(title,suburl)
  570. subs = urllib2.urlopen(suburl).read()
  571. if subs:
  572. #fname0 = re.sub("[/\n\r\t,:]"," ",title)
  573. subext = ".srt"
  574. subfile = os.path.join(downloadDir,fname0+subext)
  575. if ".xml" in suburl:
  576. subs = util.ttaf2srt(subs)
  577. with open(subfile,"w") as f:
  578. f.write(subs)
  579. else:
  580. print "\n Error downloading subtitle %s"%suburl
  581. if "nfo" in self.stream and self.stream["nfo"]:
  582. nfofile = subfile = os.path.join(downloadDir,fname0+".nfo")
  583. with open(nfofile,"w") as f:
  584. nfo_txt = util.nfo2xml(self.stream["nfo"])
  585. f.write(nfo_txt)
  586. try:
  587. h = get_header(url,headers)
  588. except:
  589. h = None
  590. if not h:
  591. self.session.open(MessageBox, "Can non get headers - %s" % url)
  592. return False
  593. mtype = h.get("content-type")
  594. if not mtype:
  595. mtype = "videp/mp4" # TODO default mtype if not content-type in header?
  596. fext,stream_type = get_ext(mtype)
  597. fname = fname0 + fext
  598. outputfile = os.path.join(downloadDir, fname)
  599. overwrite = config.plugins.playstream.overwrite_download.value
  600. if not overwrite and os.path.exists(outputfile):
  601. self.msg( _('Sorry, this file already exists:\n%s') % outputfile)
  602. return False
  603. #os.remove(outputfile)
  604. if stream_type in ("http","https", "hls"):
  605. print "\n**Download %s - %s"%(title,url)
  606. #reload(DownloadJob)
  607. try:
  608. job_manager.AddJob(DownloadJob(url, outputfile, title[:20], self.video_download_stop, headers, stream_type))
  609. except Exception as e:
  610. print str(e)
  611. traceback.print_exc()
  612. self.session.open(MessageBox, "Error - %s" % str(e), MessageBox.TYPE_INFO)
  613. return False
  614. self.active_downloads += 1
  615. #self.msg(_('Video download started!'))
  616. return True
  617. elif stream_type == "hls":
  618. #self.msg("HLS stream download not yet implemented!")
  619. print "\n**Download %s - %s"%(title,url)
  620. #reload(HLSDownloadJob)
  621. print "HLSDownload", url,outputfile
  622. job_manager.AddJob(HLSDownloadJob(url, outputfile, title[:20], self.video_download_stop))
  623. self.active_downloads += 1
  624. #self.msg(_('Video download started!'))
  625. return True
  626. elif stream_type == "rstp":
  627. self.msg("RSTP stream download not yet implemented!")
  628. return False
  629. else:
  630. self.msg("Unkown stream type - %s!"%stream_type)
  631. return False
  632. def cb_rename(self,value):
  633. if not value:
  634. return
  635. lst = self.cur_menu[1].replace("config::","")
  636. pos = self.index
  637. print value
  638. item2 = list(self.current)
  639. item2[0]=value
  640. item2 = tuple(item2)
  641. self.config.replace_item(lst,item2,pos)
  642. self.config.write_streams()
  643. self.update_content()
  644. #txt = "'%s' renamed to '%s'"%(self.current[0],value)
  645. #self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=5)
  646. def cb_favorites(self,answer):
  647. print "cb_favorites",answer,self.current
  648. if not answer:
  649. return
  650. value = answer[1]
  651. self.config.add_item(value,self.current)
  652. self.config.write_streams()
  653. txt = "'%s' added to favourite stream list '%s'"%(self.current[0],value)
  654. self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=3)
  655. #self.session.openWithCallback(self.callMyMsg, MessageBox, _("Do you want to exit the plugin?"), MessageBox.TYPE_INFO)
  656. def cb_move(self,answer):
  657. print "cb_move",answer,self.current
  658. if not answer:
  659. return
  660. answer[1]
  661. lst = self.cur_menu[1].replace("config::","")
  662. pos = self.index
  663. items = self.config.get_list_items(lst)
  664. for pos2, it in enumerate(items):
  665. if it[1] == answer[1]:
  666. break
  667. else:
  668. pos2 = None
  669. self.config.move_item(lst, pos, pos2)
  670. self.config.write_streams()
  671. self.update_content()
  672. #txt = "Item moved from position %s to %s'"%(pos,pos2)
  673. #self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=3)
  674. #self.session.openWithCallback(self.callMyMsg, MessageBox, _("Do you want to exit the plugin?"), MessageBox.TYPE_INFO)
  675. def config_screen(self):
  676. self.session.open(ConfigScreen,self)
  677. def options_screen(self):
  678. source = self.cur_menu[1].split("::")[0]
  679. options = self.sources.options_read(source)
  680. print source
  681. if not options:
  682. self.session. open(MessageBox, "No options available for source %s (%s)"%(self.cur_menu[0],source) , MessageBox.TYPE_INFO)
  683. else:
  684. self.session.open(OptionsScreen,self)
  685. def video_download_stop(self,title):
  686. self.active_downloads -= 1
  687. self.msg2(title)
  688. print "video_download_stop ", title
  689. def msg2(self,msg,timeout=10,mtype = None):
  690. mtype=mtype if mtype else MessageBox.TYPE_INFO
  691. Notifications.AddPopup(text = msg, type=mtype, timeout=timeout)
  692. def msg(self,msg,timeout=10):
  693. self.session.open(MessageBox, msg, MessageBox.TYPE_INFO, timeout)
  694. def make_service(stream):
  695. url = stream["url"]
  696. headers = []
  697. if stream.has_key("headers"):
  698. for h in stream["headers"]:
  699. headers.append("%s=%s"%(h,stream["headers"][h]))
  700. if headers:
  701. headers ="&".join(headers)
  702. url = url+"#"+headers
  703. print "stream_url with headers=",headers
  704. url = url.encode("utf8")
  705. if "|" in url:
  706. url = url.replace("|","#")
  707. service_type = 4097
  708. if re.search("\.ts$",url.split("?")[0],re.IGNORECASE):
  709. service_type = 1
  710. if "resolver" in stream and stream["resolver"] in ("hqq","viaplay","filmas"):
  711. url = util.streamproxy_encode(stream["url"], stream["headers"])
  712. #if os.path.exists("/usr/bin/exteplayer3"): # problem playing hqq streams with gstreamer, use exteplayer3
  713. #service_type = 5002
  714. print "service_type=",service_type
  715. print "stream_url=",url
  716. service = eServiceReference(service_type, 0, url)
  717. service.setName( stream["name"])
  718. return service
  719. def plugin_update(session):
  720. feed_file = "/etc/opkg/ivars777-feed.conf"
  721. if not os.path.exists(feed_file):
  722. with open(feed_file, "w") as f:
  723. f.write("src/gz ivars777 http://feed.blue.lv")
  724. cmdlist = [
  725. "opkg update",
  726. "opkg install enigma2-plugin-extensions-playstream",
  727. "echo Restart Enigma after upgrade!"
  728. ]
  729. # os.chmod(cmd, 493)
  730. session.open(Console, "Update PlayStream plugin", cmdlist)
  731. #####################################################################################################################
  732. class PSSubs(Screen):
  733. def __init__(self, session):
  734. desktopWidth = getDesktop(0).size().width()
  735. desktopHeight = getDesktop(0).size().height()
  736. offset = 20
  737. screenWidth = desktopWidth - (2 * offset)
  738. widgetWidth = screenWidth / 2 - 5
  739. self.skin = """
  740. <screen position="%d,%d" size="%d,140" zPosition="2" backgroundColor="transparent" flags="wfNoBorder">
  741. <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" />
  742. </screen>""" % (offset, desktopHeight-offset-140, screenWidth, screenWidth)
  743. self['subtitle'] = Label()
  744. Screen.__init__(self, session)
  745. #####################################################################################################################
  746. class PSPlayer(MoviePlayer):
  747. def __init__(self, session, streams):
  748. print "PSPlayer init: ",streams
  749. self.session = session
  750. self.streams = streams
  751. self.cur_stream = self.streams[0] # TODO
  752. self.selected = 0
  753. self.resume_pos = 0
  754. service = make_service(self.cur_stream)
  755. MoviePlayer.__init__(self, session, service)
  756. self.skinName = "MoviePlayer"
  757. self["actions"] = ActionMap(["MediaPlayerActions","MediaPlayerSeekActions","MoviePlayerActions","InfobarSeekActions","MovieSelectionActions","ColorActions"], {
  758. "stop":self.leavePlayer,
  759. "leavePlayer":self.leavePlayer,
  760. "audio":self.select_stream,
  761. "AudioSelection":self.select_stream,
  762. "green":self.select_stream,
  763. "subtitles":self.select_captions,
  764. "text":self.select_captions,
  765. "yellow_key":self.select_captions,
  766. "yellow":self.select_captions,
  767. "pauseServiceYellow":self.select_captions,
  768. "pause":self.select_captions,
  769. "showEventInfo":self.service_info,
  770. "info":self.service_info
  771. })
  772. self.stimer = eTimer()
  773. if "callback" in dir(self.stimer):
  774. self.stimer.callback.append(self.update_subtitles)
  775. elif "timeout" in dir(self.stimer):
  776. self.stimer_conn = self.stimer.timeout.connect(self.update_subtitles)
  777. else:
  778. self.stimer = None
  779. self.stimer_step = 500
  780. if self.cur_stream["subs"]:
  781. self.subs = self.cur_stream["subs"]
  782. self.cur_subs = 0 # TODO - no konfigurācijas
  783. self.svisible = True # TODO - no konfigurācijas
  784. self.sind = 0
  785. self.get_subs_current()
  786. else:
  787. self.subs = []
  788. self.cur_subs = 0
  789. self.svisible = False
  790. self.subtitle_window = self.session.instantiateDialog (PSSubs)
  791. self.onLayoutFinish.append(self.start_subtitles_timer)
  792. def start_subtitles_timer0(self):
  793. if self.stimer:
  794. self.subtitle_window.show()
  795. print "start_subtitles_timer"
  796. self.stimer.start(self.stimer_step)
  797. def start_subtitles_timer(self):
  798. self.stimer.start(self.stimer_step)
  799. def get_sub_pts(self,pts):
  800. sc = self.get_sub_ind(self.sind) # current subbtitle
  801. while True:
  802. if not sc:
  803. return "Error - no subs find" # TODO
  804. if pts > sc["end"]:
  805. self.sind += 1
  806. sc = self.get_sub_ind(self.sind)
  807. continue
  808. else:
  809. if pts <sc["begin"]:
  810. return " "
  811. else:
  812. txt = sc["text"] if sc["text"] else " "
  813. return txt
  814. def get_sub_ind(self,ind):
  815. subs_object = self.get_subs_current() # current subs object
  816. if subs_object:
  817. return subs_object.subs[ind] if ind<len(subs_object.subs) else None
  818. else:
  819. return None
  820. def get_subs_current(self):
  821. "Return current sub_object"
  822. if not "subs" in self.subs[self.cur_subs]:
  823. print "===== Captions to download", self.subs[self.cur_subs]["url"]
  824. subs_object = util.Captions(self.subs[self.cur_subs]["url"])
  825. print len(subs_object.subs), "items"
  826. self.subs[self.cur_subs]["subs"] = subs_object
  827. if not subs_object:
  828. return None
  829. else:
  830. return subs_object
  831. else:
  832. return self.subs[self.cur_subs]["subs"]
  833. def update_subtitles(self):
  834. if not self.shown and self.svisible:
  835. seek = self.getSeek()
  836. pos = seek.getPlayPosition()
  837. pts = pos[1]/90
  838. txt0 = "%d:%02d (%i)" % (pts/60/1000, (pts/1000)%60, pts)
  839. #print "Update_subtitles", txt0
  840. if not self.subtitle_window.shown:
  841. self.subtitle_window.show()
  842. if not self.subs:
  843. return
  844. txt = self.get_sub_pts(pts)
  845. #print "Show subtitle",txt
  846. #txt = txt0+": "+ txt
  847. self.subtitle_window["subtitle"].setText(txt)
  848. elif self.shown and self.svisible:
  849. if self.subtitle_window.shown:
  850. self.subtitle_window.hide()
  851. elif not self.svisible:
  852. if self.subtitle_window.shown:
  853. self.subtitle_window.hide()
  854. # struct SubtitleTrack
  855. # int type;
  856. # int pid;
  857. # int page_number;
  858. # int magazine_number;
  859. # std::string language_code;
  860. #selectedSubtitle = ???
  861. #self.enableSubtitle(selectedSubtitle)
  862. def play_service(self,service):
  863. self.movieSelected(service)
  864. #def doShow(self):
  865. #self.svisible = False
  866. #InfoBarShowHide.doShow(self)
  867. #def doHide(self):
  868. #self.svisible = True
  869. #InfoBarShowHide.doHide(self)
  870. def service_info(self):
  871. print "########[MoviePlayer] service_info"
  872. text = "%s\n%s %s\n%s"%(self.cur_stream["name"],self.cur_stream["lang"],self.cur_stream["quality"],self.cur_stream["desc"])
  873. text = text.encode("utf8")
  874. #print text
  875. #mtype = MessageBox.TYPE_INFO
  876. #Notifications.AddPopup(text = text, type=mtype, timeout = 10)
  877. print "StreamInfo", self.cur_stream
  878. nfo = self.cur_stream["nfo"] if "nfo" in self.cur_stream else {"movie":{"title":self.current[0],"thumb":self.current[1],"plot":self.current[3]}}
  879. self.session.open(StreamInfo, nfo)
  880. def select_stream(self):
  881. print "########[MoviePlayer] select_stream"
  882. lst = []
  883. title = "Select stream"
  884. for i,s in enumerate(self.streams):
  885. lst.append(("[%s,%s] %s"%(s["lang"],s["quality"],s["name"]),i))
  886. self.session.openWithCallback(self.cb_select_stream, ChoiceBox, title = title, list = lst,selection = self.selected)
  887. def cb_select_stream(self,answer):
  888. #print "item_menu_selected",answer
  889. if not answer:
  890. return
  891. self.selected = answer[1]
  892. service = make_service(self.streams[self.selected])
  893. self.resume_pos = self.getSeek().getPlayPosition()[1]
  894. self.play_service(service)
  895. def serviceStarted(self):
  896. print "serviceStarted"
  897. if not self.resume_pos:
  898. self.resume_pos = 0
  899. print "doSeek",self.resume_pos
  900. #self.doSeek(self.resume_pos)
  901. def select_captions(self):
  902. print "########[MoviePlayer] select_caption"
  903. lst = []
  904. title = "Select subtitles"
  905. for i,s in enumerate(self.subs):
  906. lst.append((("%s - %s"%(s["lang"],s["name"])).encode("utf8"),i))
  907. if self.svisible:
  908. selection = self.cur_subs
  909. else:
  910. selection = len(lst)
  911. lst.append(("No captions",-1))
  912. self.session.openWithCallback(self.cb_select_captions, ChoiceBox, title = title, list = lst,selection = selection)
  913. def cb_select_captions(self,answer):
  914. #print "item_menu_selected",answer
  915. if not answer:
  916. return
  917. if answer[1] == -1:
  918. self.svisible = False
  919. else:
  920. self.cur_subs = answer[1]
  921. self.svisible = True
  922. def leavePlayer(self):
  923. self.close()
  924. #self.session.openWithCallback(self.leavePlayerConfirmed, MessageBox, _("Stop playing?"))
  925. def leavePlayerConfirmed(self, answer):
  926. if answer:
  927. self.close()
  928. def doEofInternal(self, playing):
  929. self.close()
  930. #def getPluginList(self):
  931. #from Components.PluginComponent import plugins
  932. #list = []
  933. #for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
  934. #if p.name != _("TV Play"):
  935. #list.append(((boundFunction(self.getPluginName, p.name),
  936. #boundFunction(self.runPlugin, p), lambda: True), None))
  937. #return list
  938. #def showMovies(self):
  939. #pass
  940. #####################################################################################################################
  941. class ConfigScreen(ConfigListScreen,Screen):
  942. def __init__(self, session, main):
  943. #print main
  944. self.main = main
  945. self.session = session
  946. #self.setup_title = "Options"
  947. Screen.__init__(self, session)
  948. self.skinName = ["PSConfig"]
  949. self.list = [
  950. getConfigListEntry(_("Download folder"), config.plugins.playstream.download_dir),
  951. getConfigListEntry(_("Overwrite download video"), config.plugins.playstream.overwrite_download),
  952. getConfigListEntry(_("TMP folder"), config.plugins.playstream.tmp_dir),
  953. getConfigListEntry(_("Clear tmp folder on exit"), config.plugins.playstream.clear_tmp),
  954. getConfigListEntry(_("Start playstreamproxy"), config.plugins.playstream.streamproxy_start),
  955. getConfigListEntry(_("Check new plugin version in feed"), config.plugins.playstream.check_update),
  956. getConfigListEntry(_("Content config file"), config.plugins.playstream.streams_file),
  957. getConfigListEntry(_("Remote content config file"), config.plugins.playstream.streams_file_remote),
  958. getConfigListEntry(_("User remote content config file"), config.plugins.playstream.use_streams_file_remote),
  959. ]
  960. ConfigListScreen.__init__(self, self.list, session = self.session)
  961. self["title"] = Label()
  962. self["key_red"] = Button(_("Cancel"))
  963. self["key_green"] = Button(_("Save"))
  964. self["key_yellow"] = Button("Update")
  965. self["key_blue"] = Button("Downloads")
  966. self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
  967. {
  968. "red": self.cancel,
  969. "green": self.save,
  970. "yellow": self.update_plugin,
  971. "blue": self.downloads,
  972. "save": self.save,
  973. "cancel": self.cancel,
  974. "ok": self.ok,
  975. }, -2)
  976. self["title"].setText("Version %s, ivars777@gmail.com"%__version__)
  977. def getCurrentEntry(self):
  978. return self["config"].getCurrent()[0]
  979. def getCurrentValue(self):
  980. return str(self["config"].getCurrent()[1].getText())
  981. def ok(self):
  982. if self["config"].getCurrent()[1] == config.plugins.playstream.download_dir:
  983. folder = config.plugins.playstream.download_dir.value
  984. #self.session.openWithCallback(self.cb_download_dir, LocationBox,"Select Folder")
  985. self.session.openWithCallback(self.cb_download_dir, LocationBox,"Select download folder","",config.plugins.playstream.download_dir.value,config.plugins.playstream.locations,False,"Select folder",None,True,True)
  986. elif self["config"].getCurrent()[1] == config.plugins.playstream.tmp_dir:
  987. self.session.openWithCallback(self.select_tmp_dir, LocationBox,"Select tmp folder","",config.plugins.playstream.download_dir.value,config.plugins.playstream.locations,False,"Select folder",None,True,True)
  988. else:
  989. self.save()
  990. def cb_download_dir(self, folder, select=None):
  991. if not folder:
  992. return
  993. print "Folder selected - %s"%folder
  994. config.plugins.playstream.download_dir.setValue(folder)
  995. config.plugins.playstream.download_dir.save()
  996. config.plugins.playstream.locations.save()
  997. config.save()
  998. def select_tmp_dir(self, folder, select=None):
  999. if not folder:
  1000. return
  1001. print "Folder selected - %s"%folder
  1002. config.plugins.playstream.tmp_dir.setValue(folder)
  1003. config.plugins.playstream.tmp_dir.save()
  1004. config.plugins.playstream.locations.save()
  1005. config.save()
  1006. def save(self):
  1007. print "saving"
  1008. self.saveAll()
  1009. self.close(True,self.session)
  1010. self.main.update_content()
  1011. def cancel(self):
  1012. #print "cancel"
  1013. self.close(False,self.session)
  1014. def downloads(self):
  1015. self.session.open(VideoDownloadList)
  1016. def update_plugin(self):
  1017. #print "Check PlayStream for update"
  1018. self.cver = util.check_version("enigma2-plugin-extensions-playstream")
  1019. if self.cver and self.cver>__version__:
  1020. txt = "New version of plugin available for update - %s\nCurrent version - %s\nDo you want to upgrade?"%(self.cver,__version__)
  1021. else:
  1022. txt = "No new version of plugin available for update - %s\nCurrent version - %s\nDo you want to upgrade?"%(self.cver,__version__)
  1023. mbox = self.session.openWithCallback(self.update, MessageBox, txt, MessageBox.TYPE_YESNO)
  1024. mbox.setTitle("Upgrade plugin?")
  1025. def update(self,answer):
  1026. if answer:
  1027. plugin_update(self.session)
  1028. def restart_dialog(self):
  1029. self.session.openWithCallback(self.restart, MessageBox, _("Restart Enigma2?"),MessageBox.TYPE_YESNO)
  1030. def restart(self, answer):
  1031. if answer:
  1032. self.session.open(TryQuitMainloop, retvalue=3)
  1033. else:
  1034. return
  1035. #####################################################################################################################
  1036. class OptionsScreen(ConfigListScreen,Screen):
  1037. def __init__(self, session,*args):
  1038. self.session = session
  1039. Screen.__init__(self, session)
  1040. self.skinName = ["PSOptions"]
  1041. self.main = args[0]
  1042. self.setTitle("Source options")
  1043. self.source = self.main.cur_menu[1].split("::")[0]
  1044. self.cfg = ConfigSubDict() #config.plugins.playstream
  1045. self.list = []
  1046. self.options = self.main.sources.options_read(self.source)
  1047. if not self.options:
  1048. #self.session. open(MessageBox, "No options available for source %s (%s)"%(self.main.cur_menu[0],self.source) , MessageBox.TYPE_INFO)
  1049. self.close(False,self.session)
  1050. for k in self.options:
  1051. self.cfg[k]=ConfigText(default=self.options[k],fixed_size=False)
  1052. self.list.append(getConfigListEntry(k, self.cfg[k]))
  1053. ConfigListScreen.__init__(self, self.list, session = self.session)
  1054. self["title"] = Label()
  1055. self["key_red"] = Button(_("Cancel"))
  1056. self["key_green"] = Button("Save")
  1057. self["key_yellow"] = Button("")
  1058. self["key_blue"] = Button("")
  1059. self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
  1060. {
  1061. "red": self.cancel,
  1062. "green": self.save,
  1063. "save": self.save,
  1064. "cancel": self.cancel,
  1065. "ok": self.ok,
  1066. }, -2)
  1067. self["title"].setText(self.main.sources.plugins[self.source].title)
  1068. def getCurrentEntry(self):
  1069. return self["config"].getCurrent()[0]
  1070. def getCurrentValue(self):
  1071. return str(self["config"].getCurrent()[1].getText())
  1072. def ok(self):
  1073. self.save()
  1074. #if self["config"].getCurrent()[1] == config.plugins.getpicons.folder:
  1075. #folder = config.plugins.getpicons.folder.value
  1076. #self.session.openWithCallback(self.change_dir, LocationBox,"Select Folder")
  1077. #else:
  1078. #def change_dir(self, folder, select=None):
  1079. #if folder:
  1080. ##print "change_dir to %s"%folder
  1081. #config.plugins.getpicons.folder.value = folder
  1082. def save(self):
  1083. print "saving"
  1084. #self.saveAll()
  1085. for k in self.options.keys():
  1086. self.options[k]=self.cfg[k].value
  1087. print "%s=%s"%(k,self.cfg[k].value)
  1088. self.main.sources.options_write(self.source,self.options)
  1089. self.close(True,self.session)
  1090. def cancel(self):
  1091. print "cancel"
  1092. self.close(False,self.session)
  1093. #####################################################################################################################
  1094. class StreamInfo(Screen):
  1095. def __init__(self, session, nfo):
  1096. if not nfo:
  1097. return
  1098. Screen.__init__(self, session)
  1099. self.skinName = ["PSStreamInfo"]
  1100. self.session = session
  1101. self["key_red"] = Button(_("Exit"))
  1102. self["actions"] = ActionMap(["OkCancelActions", "ColorActions","ButtonSetupActions"], {
  1103. "ok": self.cancel,
  1104. "red": self.cancel,
  1105. "cancel": self.cancel,
  1106. "pageup": self.page_up,
  1107. "channelup":self.page_up,
  1108. "pagedown": self.page_down,
  1109. "channeldown":self.page_down,
  1110. })
  1111. self["pic"] = Pixmap()
  1112. self["info"] = ScrollLabel()
  1113. self["title"] = Label()
  1114. self.nfo = nfo
  1115. self.onLayoutFinish.append(self.layout_finished)
  1116. def cancel(self):
  1117. #print "cancel"
  1118. self.close(False,self.session)
  1119. def layout_finished(self):
  1120. nfo = self.nfo
  1121. print "StreamInfo nfo", nfo
  1122. if not "title" in nfo:
  1123. nfo_type, nfo = next(nfo.iteritems())
  1124. title = util.nfo2title(nfo)
  1125. desc = util.nfo2desc(nfo)
  1126. self["title"].setText(title)
  1127. self["info"].setText(desc)
  1128. image_url = nfo["thumb"]
  1129. if image_url and image_url.startswith("http"):
  1130. fname = image_url.replace(":", "-").replace("/", "_")
  1131. image_path = os.path.join(config.plugins.playstream.tmp_dir.value, fname)
  1132. if not os.path.exists(image_path):
  1133. downloadPage(image_url, image_path, CustomContextFactory(image_url.split("/")[2])).addCallback(boundFunction(self.download_finished, image_path,image_url)).addErrback(boundFunction(self.download_failed, image_path,image_url))
  1134. else:
  1135. self.download_finished(image_path, image_url)
  1136. elif image_url:
  1137. image_path = os.path.join(cur_directory, image_url)
  1138. self.download_finished(image_path,"")
  1139. def download_finished(self, image_path,image_url):
  1140. sc = AVSwitch().getFramebufferScale()
  1141. self.picloads = ePicLoad()
  1142. try:
  1143. self.picloads.PictureData.get().append(boundFunction(self.finish_decode, image_path))
  1144. except:
  1145. self.picloads.PictureData.connect(boundFunction(self.finish_decode, image_path))
  1146. # 0=Width 1=Height 2=Aspect 3=use_cache 4=resize_type 5=Background(#AARRGGBB)
  1147. self.picloads.setPara((self["pic"].instance.size().width(), self["pic"].instance.size().height(),
  1148. sc[0], sc[1], True, 0, "#00000000"))
  1149. print image_path,image_url
  1150. self.picloads.startDecode(image_path.encode("utf8"))
  1151. def download_failed(self, image_path,image_url):
  1152. print "[PlayStream] Image downloaded failed ",image_url
  1153. def finish_decode(self, image_path,picInfo = None):
  1154. ptr = self.picloads.getData()
  1155. self["pic"].instance.setPixmap(ptr)
  1156. def page_down(self):
  1157. self["info"].pageDown()
  1158. def page_up(self):
  1159. self["info"].pageUp()