#!/usr/bin/env python # coding=utf8 # This file is part of PlayStream - enigma2 plughhhhin to play video streams from various sources # Copyright (c) 2016 ivars777 (ivars777@gmail.com) # Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html # Used fragments of code from enigma2-plugin-tv3play by Taapat (https://github.com/Taapat/enigma2-plugin-tv3play) # __version__ = "0.8t" __id__ = "playstream" __title__ = "PlayStream" __author__ = "ivars777@gmail.com" __desc__ = "Play online video streams from various sources" import os,time,sys,os,os.path import datetime,re, traceback import urllib2 from Plugins.Plugin import PluginDescriptor from enigma import ePicLoad, eServiceReference, eTimer, getDesktop from Screens.Screen import Screen from Screens.InfoBarGenerics import InfoBarShowHide from Screens.InfoBar import MoviePlayer from Screens.MessageBox import MessageBox from Screens.LocationBox import LocationBox from Screens.ChoiceBox import ChoiceBox from Screens.VirtualKeyBoard import VirtualKeyBoard from Screens.Console import Console from Screens.Standby import TryQuitMainloop #from Screens.InputBox import InputBox from Components.AVSwitch import AVSwitch from Components.ActionMap import ActionMap, HelpableActionMap from Components.Label import Label from Components.ScrollLabel import ScrollLabel from Components.Input import Input from Components.MenuList import MenuList from Components.Pixmap import Pixmap from Components.Sources.List import List from Components.Sources.StaticText import StaticText from Components.Button import Button from Components.Task import job_manager from Components.config import config, ConfigSubsection, ConfigText, ConfigInteger, ConfigLocations, ConfigDirectory, ConfigSet, ConfigYesNo, ConfigSelection, getConfigListEntry, ConfigSelectionNumber, ConfigNumber from Components.config import config, ConfigSubsection, ConfigYesNo, getConfigListEntry, ConfigSelection, ConfigNumber, ConfigDirectory,ConfigText, ConfigSubDict from Components.ConfigList import ConfigListScreen #from Screens.LocationBox import LocationBox from Tools import Notifications from Tools.BoundFunction import boundFunction from Tools.Directories import resolveFilename, SCOPE_PLUGINS from Tools.LoadPixmap import LoadPixmap from skin import loadSkin from twisted.web.client import downloadPage,defer,reactor try: # available since twisted 14.0 from twisted.internet._sslverify import ClientTLSOptions except ImportError: ClientTLSOptions = None from twisted.internet.ssl import ClientContextFactory from plugin_locale import _ from content import ContentSources from content import util from VideoDownload import DownloadJob, HLSDownloadJob,VideoDownloadList from content.Downloader import get_header, get_ext #import enigma2_api e2 = None cur_directory = os.path.dirname(os.path.realpath(__file__)) loadSkin(os.path.join(cur_directory, "skin.xml")) ##################################################################################################################### config.plugins.playstream = ConfigSubsection() config.plugins.playstream.locations = ConfigLocations(default=["/media/hdd/movie/"]) config.plugins.playstream.download_dir = ConfigDirectory(default="/media/hdd/movie/") config.plugins.playstream.tmp_dir = ConfigDirectory(default="/tmp/playstream/") config.plugins.playstream.streamproxy_start = ConfigYesNo(default = True) config.plugins.playstream.overwrite_download = ConfigYesNo(default = False) #config.plugins.playstream.size = ConfigSelection({"400x240":"400x240","220x132":"220x132","100x60":"100x60"}, default="220x132") config.plugins.playstream.clear_tmp = ConfigYesNo(default = True) config.plugins.playstream.check_update = ConfigYesNo(default = True) config.plugins.playstream.streams_file = ConfigText(default="streams.cfg") config.plugins.playstream.streams_file_remote = ConfigText(default="ftp://user:password@host/hdd/streams.cfg") config.plugins.playstream.use_streams_file_remote = ConfigYesNo(default = False) class CustomContextFactory(ClientContextFactory): def __init__(self, hostname = None): self.hostname = hostname def getContext(self): ctx = self._contextFactory(self.method) if self.hostname and ClientTLSOptions: ClientTLSOptions(self.hostname, ctx) return ctx ##################################################################################################################### class MainScreen(Screen): def __init__(self, session): Screen.__init__(self, session) self.skinName = ["PSMain"] self.session = session self.e2 = None self["key_red"] = Button(_("Exit")) self["key_green"] = Button(_("Select")) self["key_yellow"] = Button(_("Options")) self["key_blue"] = Button(_("Config")) self["key_menu"] = Button("Menu") self["key_exit"] = Button("Back") self["actions"] = ActionMap(["OkCancelActions", "ColorActions","MenuActions", "NumberActions", "WizardActions","ButtonSetupActions"], { #"cancel": self.Cancel, "ok": self.ok, "green": self.ok, "red": self.cancel, "yellow": self.options_screen, "blue": self.config_screen, "menu": self.item_menu, "cancel": self.back, "info": self.show_info, "pageup": self.page_up, "channelup":self.page_up, "pagedown": self.page_down, "channeldown":self.page_down, }) self["list"] = List([]) self["list"].onSelectionChanged.append(self.selection_changed) self["pic"] = Pixmap() self["cur"] = ScrollLabel() self["title"] = Label() self.downloading = 0 self.active_downloads = 0 if not os.path.exists(config.plugins.playstream.tmp_dir.value): os.mkdir(config.plugins.playstream.tmp_dir.value) self.onLayoutFinish.append(self.layout_finished) def layout_finished(self): self.defimage = LoadPixmap(os.path.join(cur_directory,"PlayStream.png")) #self.defimage = None #sc = AVSwitch().getFramebufferScale() #self.defimage0 = ePicLoad() #self.defimage0.PictureData.get().append(boundFunction(self.FinishDecodeDef, os.path.join(cur_directory,"PlayStream.png"))) #self.defimage0.setPara((self["pic"].instance.size().width(),self["pic"].instance.size().height(),sc[0], sc[1], False, 0, "#00000000")) #self.defimage0.startDecode("default") self.active_downloads = 0 self.images = {} self.images_url = {} self.picloads = {} self.history = [] reload(ContentSources) sources_directory = os.path.join(cur_directory, "content", "sources") print "**** use_streams_file_remote=", config.plugins.playstream.use_streams_file_remote.value print "**** streams_file_remote=", config.plugins.playstream.streams_file_remote.value print "**** streams_file=", config.plugins.playstream.streams_file.value print "**** sources_directory=", sources_directory if config.plugins.playstream.use_streams_file_remote.value: try: self.sources = ContentSources.ContentSources(sources_directory, config.plugins.playstream.streams_file_remote.value) print "Remote stream file opened" except Exception as e: try: self.sources = ContentSources.ContentSources(sources_directory, config.plugins.playstream.streams_file.value) print "Streams_file fallback" self.msg2("Remote streams file is not available, fallback to local") except Exception as e: self.msg2(e.message) else: try: self.sources = ContentSources.ContentSources(sources_directory, config.plugins.playstream.streams_file.value) print "Local stream file opened" except Exception as e: self.msg2(e.message) self.cancel() if "config" not in self.sources.plugins: self.msg2("No streams config file") self.cancel() self.config = self.sources.plugins["config"] self.cur_menu = ("Home","config::home","","Sākums") # self.content = self.sources.get_content(self.cur_menu[1]) #print self.content self["list"].setList(self.content) self["cur"].setText(self.content[0][3]) self.setTitle2(self.cur_menu[0] + " (" + self.cur_menu[1].split("::")[0]+")") self.show_picture(self.content[0][2]) reactor.callLater(1,self.check_update) def check_update(self): #print "Check PlayStream for update" if config.plugins.playstream.check_update: self.cver = util.check_version("enigma2-plugin-extensions-playstream") if self.cver and self.cver>__version__: txt = "New version of plugin available for update - %s\nCurrent version - %s\nDo you want to upgrade?"%(self.cver,__version__) mbox = self.session.openWithCallback(self.update, MessageBox, txt, MessageBox.TYPE_YESNO) mbox.setTitle("Upgrade plugin?") def update(self,answer): if answer: plugin_update(self.session) def page_down(self): self["cur"].pageDown() def page_up(self): self["cur"].pageUp() def selection_changed(self): current = self["list"].getCurrent() print "[PlayStream] SelectionChanged: current=",current[1] if not current: return self["cur"].setText(current[3]) if current[3] else self["cur"].setText("") if current[2]: self.show_picture(current[2]) else: self.show_default_picture() def setTitle2(self,title): #print self.keys() self["title"].setText(title) def show_default_picture(self): if self.defimage: self["pic"].instance.setPixmap(self.defimage) def show_picture(self, image_url): if image_url == "default": self.show_default_picture() return elif self.images.has_key(image_url): if self.images[image_url] in (-1,-2): return elif self.images[image_url] == -3: self.show_default_picture() return else: self["pic"].instance.setPixmap(self.images[image_url]) else: if image_url.startswith("http"): fname = image_url.replace(":","-").replace("/","_") image_path = os.path.join(config.plugins.playstream.tmp_dir.value, fname) self.download_image(image_path, image_url) else: # local file image_path = os.path.join(cur_directory, "picons", image_url) self.start_decode(image_path,image_url) def start_decode(self,image_path,image_url): self.images[image_url] = -2 self.images_url[image_path] = image_url sc = AVSwitch().getFramebufferScale() if not self.picloads.has_key(image_path): self.picloads[image_path] = ePicLoad() try: self.picloads[image_path].PictureData.get().append(boundFunction(self.decode_finished, image_path)) except: self.picloads[image_path].PictureData.connect(boundFunction(self.decode_finished, image_path)) self.picloads[image_path].setPara((self["pic"].instance.size().width(), self["pic"].instance.size().height(), sc[0], sc[1], True, 0, "#00000000")) print image_path,image_url self.picloads[image_path].startDecode(image_path) def decode_finished(self, image_path, picInfo = None): image_url = self.images_url[image_path] del self.images_url[image_path] #III self.images[image_url] = self.picloads[image_path].getData() self["pic"].instance.setPixmap(self.images[image_url]) del self.picloads[image_path] if len(self.images)>30: del self.images[self.images.keys()[0]] # self.images.pop() #def FinishDecodeDef(self, image_path,picInfo = None): # self.defimage = self.defimage0.getData() # del self.defimage0 # self["pic"].instance.setPixmap(self.defimage) def download_image(self,image_path,image_url): #print "Image download started",self.downloading,image_path,image_url self.downloading += 1 self.images[image_url] = -1 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)) def download_image_finished(self, image_path, image_url, result): self.downloading -= 1 #print "[ Play] Image downloaded finished ",self.downloading,image_path, image_url,result self.start_decode(image_path,image_url) def download_image_failed(self, image_path, image_url, result): self.downloading -= 1 print "[TV Play] Image downloaded failed ",self.downloading,image_path, image_url,result self.images[image_url] = -3 def ok(self): current = self["list"].getCurrent() self.current = current index = self["list"].getIndex() self.index = index print "[PlayStream] - menu selected ", current data = current[1].split("::")[1] if "::" in current[1] else current[1] if not data: return if self.sources.is_video(current[1]): if ContentSources.stream_type(current[1]): stream = util.item() stream["url"] = current[1] stream["name"] = current[0] streams = [stream] else: try: streams = self.sources.get_streams(current[1]) except Exception,e: print str(e) traceback.print_exc() self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO) return if streams: #print streams for s in streams: if not s["name"]: s["name"] = current[0] if not s["img"]: s["img"] = current[2] if not s["desc"]: s["desc"] = current[3] try: self.session.open(PSPlayer, streams) except Exception as e: traceback.print_exc() self.msg("Error launching player - " + str(e)) return else: self.msg("No stream found - %s"%(self.current[1])) return elif current[1] == "back": cur_menu_old = self.cur_menu self.cur_menu = self.history.pop() self.update_streams_file() new_content = self.sources.get_content(self.cur_menu[1]) try: index = zip(*new_content)[1].index(cur_menu_old[1]) except: index = 0 self.setTitle2(self.cur_menu[0]+ " (" + self.cur_menu[1].split("::")[0]+")") self.show_content(new_content,index) else: print "selected=",current if "{0}" in current[1]: self.session.openWithCallback(self.cb_input,VirtualKeyBoard, title="Enter value", text="") #a = raw_input("Enter value:") #a = "big bang" #current = (current[0],current[1].format(a),current[2],current[3]) #self.get_content(current) else: self.history.append(self.cur_menu) self.update_content(current) def cb_input(self,value): if not value: return current = self.current current = (current[0],current[1].format(value),current[2],current[3]) self.history.append(self.cur_menu) self.update_content(current) def update_content(self,current=None): if current: self.cur_menu = current self.update_streams_file() try: new_content = self.sources.get_content(self.cur_menu[1]) except Exception,e: self.cur_menu = self.history.pop() traceback.print_exc() self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO) return self.setTitle2(self.cur_menu[0] + " (" + self.cur_menu[1].split("::")[0]+")") self.show_content(new_content) def show_content(self,content,index=0): self["list"].setList(content) self["list"].setIndex(index) self.selection_changed() def show_info(self): self.current = self["list"].getCurrent() data = self.current[1] source = data.split("::")[0] if self.sources.is_video(data) and "get_info" in dir(self.sources.plugins[source]): #self.sources.plugins try: nfo = self.sources.get_info(self.current[1]) except Exception, e: print str(e) traceback.print_exc() self.session.open(MessageBox, "Error - %s" % str(e), MessageBox.TYPE_INFO) return else: nfo = {"movie": {"title": self.current[0], "thumb": self.current[2], "plot": self.current[3]}} self.session.open(StreamInfo, nfo) def back(self): if self.history: self["list"].setIndex(0) self.ok() else: self.cancel() def update_streams_file(self): if config.plugins.playstream.use_streams_file_remote.value: self.config.set_streams_file(config.plugins.playstream.streams_file_remote.value) else: self.config.set_streams_file(config.plugins.playstream.streams_file.value) def cancel(self): print "Exiting PlayStream" if config.plugins.playstream.clear_tmp.value and os.path.exists(config.plugins.playstream.tmp_dir.value): for name in os.listdir(config.plugins.playstream.tmp_dir.value): #print "remove "+os.path.join(config.plugins.playstream.tmp_dir.value, name) os.remove(os.path.join(config.plugins.playstream.tmp_dir.value, name)) #os.rmdir(config.plugins.playstream.tmp_dir.value) self.close() def item_menu(self): print "\n[PlayStream] options\n" self.current = self["list"].getCurrent() self.index = self["list"].getIndex() #self.session. open(MessageBox, "Item options - %s"%current[0] , MessageBox.TYPE_INFO) #args=[current,self.cur_menu] #self.session.open(ItemMenuScreen,current,index,self) lst = [ ("Aditional information","info","Display additional information about item"), ("Add to bouquet","bouquet","Add current item to Enigma2 bouquet"), ("Add to favorites","favorites","Add current item to PlayStrem favorites"), ] if "config::" in self.cur_menu[1]: lst.extend([ ("Rename item","rename","Rename list item"), ("Move item","move","Move list item"), ("Delete item","delete","Delete list item"), ("Add submenu","add_list","Add submenu before selected item"), ]) lst.extend([ ("Show active downloads","download_list","Show active downloads list"), ("Set download folder","download_folder","Set download folder") ]) if self.sources.is_video(self.current[1]): lst.extend([ ("Download video to default folder","download","Download video to default folder"), ("Download video, ask folder","download2","Download video, ask download folder"), ("Download video to subfolder, ask parent folder","download3","Download video to subfolder, ask download folder"), ]) else: lst.extend([ ("Download list to default folder","download","Download videos in list (if any)"), ("Download list, ask folder","download2","Download videos in list (if any), ask download folder"), ]) title = self.current[0] self.session.openWithCallback(self.cb_item_menu, ChoiceBox, title = title, list = lst) #TODO def cb_item_menu(self,answer): #print "item_menu_selected",answer if not answer: return self.answer = answer[1] if answer[1] == "info": #self.show_nfo() #self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO) self.show_info() elif answer[1] == "bouquet": #if not e2: #e2 = enigma2_api.DBServices() #print "load_buuquets - ",e2._load_bouquets() self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO) elif answer[1] == "favorites": lists = self.config.get_lists() lists2 = [(self.config.get_title(l),l) for l in lists] #print lists2 self.session.openWithCallback(self.cb_favorites, ChoiceBox, title="Selected menu item will be added",list = lists2) elif answer[1] == "delete": lst = self.cur_menu[1].replace("config::","") #print lst self.config.del_item(lst,self.index) self.config.write_streams() self.update_content() txt = "'%s' deleted from favourite stream list '%s'"%(self.current[0],lst) self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=5) elif answer[1] == "move": lst = self.cur_menu[1].replace("config::","") items = self.config.get_list_items(lst) self.session.openWithCallback(self.cb_move, ChoiceBox, title="Move before selected item",list = items) #self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO) #self.config.write_streams() #txt = "'%s' deleted from favourite stream list '%s'"%(self.current[0],lst) #self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=5) elif answer[1] == "rename": #name2 = "Renamed" self.session.openWithCallback(self.cb_rename,VirtualKeyBoard, title="Enter new item name", text=self.current[0]) elif answer[1] == "add_list": self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO,timeout=5) pass #TODO elif answer[1] in ("download2","download3"): # ask download folder before 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) elif answer[1] == 'download': self.download_prepare(config.plugins.playstream.download_dir.value) elif answer[1] == 'download_list': self.download_list() elif answer[1] == 'download_folder': #downloadDir = "/media/hdd/movie" #config.plugins.playstream.downloadDir.value TODO 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) return def download_prepare(self, download_dir=""): current = self.current if not download_dir: return #download_dir = config.plugins.playstream.download_dir.value title = re.sub("\[.+?\]","", util.make_fname(current[0])).strip() download_dir2 = os.path.join(download_dir, title) if not self.sources.is_video(current[1]): #self.msg("Can not download listst (yet) - %s"%(current[1])) self.update_streams_file() n = 0 for current2 in self.sources.get_content(current[1]): if self.sources.is_video(current2[1]): n += 1 self.download_video(current2,download_dir2) if n>0: self.msg("%s videos download started to %s"%(n,download_dir2)) else: self.msg("No videos to download") else: if self.answer == "download": download_dir2 = download_dir if self.download_video(current,download_dir2): self.msg("Video download started to %s"%download_dir2) def cb_download_dir(self, downloadDir, select=None): if not downloadDir: return print "Folder selected - %s"%downloadDir config.plugins.playstream.download_dir.setValue(downloadDir) config.plugins.playstream.download_dir.save() config.plugins.playstream.locations.save() config.save() def download_list(self): self.session.open(VideoDownloadList) def download_video(self,current, download_dir=""): if ContentSources.stream_type(current[1]): stream = util.item() stream["url"] = current[1] stream["name"] = current[0] streams = [stream] else: try: streams = self.sources.get_streams(current[1]) except Exception,e: print "Error - %s"%str(e) traceback.print_exc() self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO) return if not streams: self.msg("No stream found to download - %s"%(self.current[1])) return for s in streams: if not s["name"]: s["name"] = current[0] if not s["img"]: s["img"] = current[2] if not s["desc"]: s["desc"] = current[3] if len(streams)>1: stream = streams[0] # TODO iespeja izvelēties strīmu, ja to ir vairāki else: stream = streams[0] stream = util.stream_change(stream) return self.download_stream(stream, download_dir) def download_stream(self,stream,download_dir=""): print "download stream",stream #self.msg("Start downloading..") self.stream = stream url = stream["url"] headers = stream["headers"] if "headers" in stream else {} stream_type = ContentSources.stream_type(url) #self.sources.stream_type(stream["url"]) if not stream_type: # print "Not supported stream type found to download - %s"%(self.current[1]) self.msg("Not supported stream type found to download - %s"%(self.current[1])) return title = self.stream["name"].strip() downloadDir = config.plugins.playstream.download_dir.value if not download_dir else download_dir if not os.path.exists(downloadDir): try: os.mkdir(downloadDir) except Exception as e: traceback.print_exc() print 'Error creating download directory "%s"!\nPlease specify in the settings existing directory\n%s'%(downloadDir,str(e)) self.msg('Error creating download directory "%s"!\nPlease specify in the settings existing directory\n%s'%(downloadDir,str(e))) return fname0 = util.make_fname(title) #print "Trying to download - ", current if self.stream["img"]: try: thumb = urllib2.urlopen(stream["img"]).read() except: thumb = "" if thumb: imgext = ".jpg" m = re.search("(\.\w{3})$",stream["img"]) if m: imgext = m.group(1) imgfile = os.path.join(downloadDir,fname0+imgext) with open(imgfile,"wb") as f: f.write(thumb) if self.stream["subs"]: suburl = self.stream["subs"][0]["url"] print "\n**Download subtitles %s - %s"%(title,suburl) subs = urllib2.urlopen(suburl).read() if subs: #fname0 = re.sub("[/\n\r\t,:]"," ",title) subext = ".srt" subfile = os.path.join(downloadDir,fname0+subext) if ".xml" in suburl: subs = util.ttaf2srt(subs) with open(subfile,"w") as f: f.write(subs) else: print "\n Error downloading subtitle %s"%suburl if "nfo" in self.stream and self.stream["nfo"]: nfofile = subfile = os.path.join(downloadDir,fname0+".nfo") with open(nfofile,"w") as f: nfo_txt = util.nfo2xml(self.stream["nfo"]) f.write(nfo_txt) try: h = get_header(url,headers) except: h = None if not h: self.session.open(MessageBox, "Can non get headers - %s" % url) return False mtype = h.get("content-type") if not mtype: mtype = "videp/mp4" # TODO default mtype if not content-type in header? fext,stream_type = get_ext(mtype) fname = fname0 + fext outputfile = os.path.join(downloadDir, fname) overwrite = config.plugins.playstream.overwrite_download.value if not overwrite and os.path.exists(outputfile): self.msg( _('Sorry, this file already exists:\n%s') % outputfile) return False #os.remove(outputfile) if stream_type in ("http","https", "hls"): print "\n**Download %s - %s"%(title,url) #reload(DownloadJob) try: job_manager.AddJob(DownloadJob(url, outputfile, title[:20], self.video_download_stop, headers, stream_type)) except Exception as e: print str(e) traceback.print_exc() self.session.open(MessageBox, "Error - %s" % str(e), MessageBox.TYPE_INFO) return False self.active_downloads += 1 #self.msg(_('Video download started!')) return True elif stream_type == "hls": #self.msg("HLS stream download not yet implemented!") print "\n**Download %s - %s"%(title,url) #reload(HLSDownloadJob) print "HLSDownload", url,outputfile job_manager.AddJob(HLSDownloadJob(url, outputfile, title[:20], self.video_download_stop)) self.active_downloads += 1 #self.msg(_('Video download started!')) return True elif stream_type == "rstp": self.msg("RSTP stream download not yet implemented!") return False else: self.msg("Unkown stream type - %s!"%stream_type) return False def cb_rename(self,value): if not value: return lst = self.cur_menu[1].replace("config::","") pos = self.index print value item2 = list(self.current) item2[0]=value item2 = tuple(item2) self.config.replace_item(lst,item2,pos) self.config.write_streams() self.update_content() #txt = "'%s' renamed to '%s'"%(self.current[0],value) #self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=5) def cb_favorites(self,answer): print "cb_favorites",answer,self.current if not answer: return value = answer[1] self.config.add_item(value,self.current) self.config.write_streams() txt = "'%s' added to favourite stream list '%s'"%(self.current[0],value) self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=3) #self.session.openWithCallback(self.callMyMsg, MessageBox, _("Do you want to exit the plugin?"), MessageBox.TYPE_INFO) def cb_move(self,answer): print "cb_move",answer,self.current if not answer: return answer[1] lst = self.cur_menu[1].replace("config::","") pos = self.index items = self.config.get_list_items(lst) for pos2, it in enumerate(items): if it[1] == answer[1]: break else: pos2 = None self.config.move_item(lst, pos, pos2) self.config.write_streams() self.update_content() #txt = "Item moved from position %s to %s'"%(pos,pos2) #self.session.open(MessageBox, txt, MessageBox.TYPE_INFO,timeout=3) #self.session.openWithCallback(self.callMyMsg, MessageBox, _("Do you want to exit the plugin?"), MessageBox.TYPE_INFO) def config_screen(self): self.session.open(ConfigScreen,self) def options_screen(self): source = self.cur_menu[1].split("::")[0] options = self.sources.options_read(source) print source if not options: self.session. open(MessageBox, "No options available for source %s (%s)"%(self.cur_menu[0],source) , MessageBox.TYPE_INFO) else: self.session.open(OptionsScreen,self) def video_download_stop(self,title): self.active_downloads -= 1 self.msg2(title) print "video_download_stop ", title def msg2(self,msg,timeout=10,mtype = None): mtype=mtype if mtype else MessageBox.TYPE_INFO Notifications.AddPopup(text = msg, type=mtype, timeout=timeout) def msg(self,msg,timeout=10): self.session.open(MessageBox, msg, MessageBox.TYPE_INFO, timeout) def make_service(stream): url = stream["url"] headers = [] if stream.has_key("headers"): for h in stream["headers"]: headers.append("%s=%s"%(h,stream["headers"][h])) if headers: headers ="&".join(headers) url = url+"#"+headers print "stream_url with headers=",headers url = url.encode("utf8") if "|" in url: url = url.replace("|","#") service_type = 4097 if re.search("\.ts$",url.split("?")[0],re.IGNORECASE): service_type = 1 if "resolver" in stream and stream["resolver"] in ("hqq","viaplay","filmas"): url = util.streamproxy_encode(stream["url"], stream["headers"]) #if os.path.exists("/usr/bin/exteplayer3"): # problem playing hqq streams with gstreamer, use exteplayer3 #service_type = 5002 print "service_type=",service_type print "stream_url=",url service = eServiceReference(service_type, 0, url) service.setName( stream["name"]) return service def plugin_update(session): feed_file = "/etc/opkg/ivars777-feed.conf" if not os.path.exists(feed_file): with open(feed_file, "w") as f: f.write("src/gz ivars777 http://feed.blue.lv") cmdlist = [ "opkg update", "opkg install enigma2-plugin-extensions-playstream", "echo Restart Enigma after upgrade!" ] # os.chmod(cmd, 493) session.open(Console, "Update PlayStream plugin", cmdlist) ##################################################################################################################### class PSSubs(Screen): def __init__(self, session): desktopWidth = getDesktop(0).size().width() desktopHeight = getDesktop(0).size().height() offset = 20 screenWidth = desktopWidth - (2 * offset) widgetWidth = screenWidth / 2 - 5 self.skin = """ """ % (offset, desktopHeight-offset-140, screenWidth, screenWidth) self['subtitle'] = Label() Screen.__init__(self, session) ##################################################################################################################### class PSPlayer(MoviePlayer): def __init__(self, session, streams): print "PSPlayer init: ",streams self.session = session self.streams = streams self.cur_stream = self.streams[0] # TODO self.selected = 0 self.resume_pos = 0 service = make_service(self.cur_stream) MoviePlayer.__init__(self, session, service) self.skinName = "MoviePlayer" self["actions"] = ActionMap(["MediaPlayerActions","MediaPlayerSeekActions","MoviePlayerActions","InfobarSeekActions","MovieSelectionActions","ColorActions"], { "stop":self.leavePlayer, "leavePlayer":self.leavePlayer, "audio":self.select_stream, "AudioSelection":self.select_stream, "green":self.select_stream, "subtitles":self.select_captions, "text":self.select_captions, "yellow_key":self.select_captions, "yellow":self.select_captions, "pauseServiceYellow":self.select_captions, "pause":self.select_captions, "showEventInfo":self.service_info, "info":self.service_info }) self.stimer = eTimer() if "callback" in dir(self.stimer): self.stimer.callback.append(self.update_subtitles) elif "timeout" in dir(self.stimer): self.stimer_conn = self.stimer.timeout.connect(self.update_subtitles) else: self.stimer = None self.stimer_step = 500 if self.cur_stream["subs"]: self.subs = self.cur_stream["subs"] self.cur_subs = 0 # TODO - no konfigurācijas self.svisible = True # TODO - no konfigurācijas self.sind = 0 self.get_subs_current() else: self.subs = [] self.cur_subs = 0 self.svisible = False self.subtitle_window = self.session.instantiateDialog (PSSubs) self.onLayoutFinish.append(self.start_subtitles_timer) def start_subtitles_timer0(self): if self.stimer: self.subtitle_window.show() print "start_subtitles_timer" self.stimer.start(self.stimer_step) def start_subtitles_timer(self): self.stimer.start(self.stimer_step) def get_sub_pts(self,pts): sc = self.get_sub_ind(self.sind) # current subbtitle while True: if not sc: return "Error - no subs find" # TODO if pts > sc["end"]: self.sind += 1 sc = self.get_sub_ind(self.sind) continue else: if pts __version__: txt = "New version of plugin available for update - %s\nCurrent version - %s\nDo you want to upgrade?"%(self.cver,__version__) else: txt = "No new version of plugin available for update - %s\nCurrent version - %s\nDo you want to upgrade?"%(self.cver,__version__) mbox = self.session.openWithCallback(self.update, MessageBox, txt, MessageBox.TYPE_YESNO) mbox.setTitle("Upgrade plugin?") def update(self,answer): if answer: plugin_update(self.session) def restart_dialog(self): self.session.openWithCallback(self.restart, MessageBox, _("Restart Enigma2?"),MessageBox.TYPE_YESNO) def restart(self, answer): if answer: self.session.open(TryQuitMainloop, retvalue=3) else: return ##################################################################################################################### class OptionsScreen(ConfigListScreen,Screen): def __init__(self, session,*args): self.session = session Screen.__init__(self, session) self.skinName = ["PSOptions"] self.main = args[0] self.setTitle("Source options") self.source = self.main.cur_menu[1].split("::")[0] self.cfg = ConfigSubDict() #config.plugins.playstream self.list = [] self.options = self.main.sources.options_read(self.source) if not self.options: #self.session. open(MessageBox, "No options available for source %s (%s)"%(self.main.cur_menu[0],self.source) , MessageBox.TYPE_INFO) self.close(False,self.session) for k in self.options: self.cfg[k]=ConfigText(default=self.options[k],fixed_size=False) self.list.append(getConfigListEntry(k, self.cfg[k])) ConfigListScreen.__init__(self, self.list, session = self.session) self["title"] = Label() self["key_red"] = Button(_("Cancel")) self["key_green"] = Button("Save") self["key_yellow"] = Button("") self["key_blue"] = Button("") self["setupActions"] = ActionMap(["SetupActions", "ColorActions"], { "red": self.cancel, "green": self.save, "save": self.save, "cancel": self.cancel, "ok": self.ok, }, -2) self["title"].setText(self.main.sources.plugins[self.source].title) def getCurrentEntry(self): return self["config"].getCurrent()[0] def getCurrentValue(self): return str(self["config"].getCurrent()[1].getText()) def ok(self): self.save() #if self["config"].getCurrent()[1] == config.plugins.getpicons.folder: #folder = config.plugins.getpicons.folder.value #self.session.openWithCallback(self.change_dir, LocationBox,"Select Folder") #else: #def change_dir(self, folder, select=None): #if folder: ##print "change_dir to %s"%folder #config.plugins.getpicons.folder.value = folder def save(self): print "saving" #self.saveAll() for k in self.options.keys(): self.options[k]=self.cfg[k].value print "%s=%s"%(k,self.cfg[k].value) self.main.sources.options_write(self.source,self.options) self.close(True,self.session) def cancel(self): print "cancel" self.close(False,self.session) ##################################################################################################################### class StreamInfo(Screen): def __init__(self, session, nfo): if not nfo: return Screen.__init__(self, session) self.skinName = ["PSStreamInfo"] self.session = session self["key_red"] = Button(_("Exit")) self["actions"] = ActionMap(["OkCancelActions", "ColorActions","ButtonSetupActions"], { "ok": self.cancel, "red": self.cancel, "cancel": self.cancel, "pageup": self.page_up, "channelup":self.page_up, "pagedown": self.page_down, "channeldown":self.page_down, }) self["pic"] = Pixmap() self["info"] = ScrollLabel() self["title"] = Label() self.nfo = nfo self.onLayoutFinish.append(self.layout_finished) def cancel(self): #print "cancel" self.close(False,self.session) def layout_finished(self): nfo = self.nfo print "StreamInfo nfo", nfo if not "title" in nfo: nfo_type, nfo = next(nfo.iteritems()) title = util.nfo2title(nfo) desc = util.nfo2desc(nfo) self["title"].setText(title) self["info"].setText(desc) image_url = nfo["thumb"] if image_url and image_url.startswith("http"): fname = image_url.replace(":", "-").replace("/", "_") image_path = os.path.join(config.plugins.playstream.tmp_dir.value, fname) if not os.path.exists(image_path): 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)) else: self.download_finished(image_path, image_url) elif image_url: image_path = os.path.join(cur_directory, image_url) self.download_finished(image_path,"") def download_finished(self, image_path,image_url): sc = AVSwitch().getFramebufferScale() self.picloads = ePicLoad() try: self.picloads.PictureData.get().append(boundFunction(self.finish_decode, image_path)) except: self.picloads.PictureData.connect(boundFunction(self.finish_decode, image_path)) # 0=Width 1=Height 2=Aspect 3=use_cache 4=resize_type 5=Background(#AARRGGBB) self.picloads.setPara((self["pic"].instance.size().width(), self["pic"].instance.size().height(), sc[0], sc[1], True, 0, "#00000000")) print image_path,image_url self.picloads.startDecode(image_path.encode("utf8")) def download_failed(self, image_path,image_url): print "[PlayStream] Image downloaded failed ",image_url def finish_decode(self, image_path,picInfo = None): ptr = self.picloads.getData() self["pic"].instance.setPixmap(ptr) def page_down(self): self["info"].pageDown() def page_up(self): self["info"].pageUp()