#!/usr/bin/env python # coding=utf8 # # # This file is part of PlayStream - enigma2 plugin 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) # import os,time,sys,os,os.path import datetime,re from enigma import ePicLoad, eServiceReference, eTimer from Components.ActionMap import ActionMap,HelpableActionMap from Components.AVSwitch import AVSwitch from Components.Label import Label 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 Plugins.Plugin import PluginDescriptor from Screens.InfoBar import MoviePlayer from Screens.MessageBox import MessageBox #from Screens.InputBox import InputBox from Screens.ChoiceBox import ChoiceBox from Screens.VirtualKeyBoard import VirtualKeyBoard from Components.Input import Input from Screens.Screen import Screen from Tools.BoundFunction import boundFunction from Tools.Directories import resolveFilename, SCOPE_PLUGINS from Tools.LoadPixmap import LoadPixmap from twisted.web.client import downloadPage,defer,reactor from Components.Task import job_manager import ContentSources from VideoDownload import downloadJob, HLSDownloadJob,VideoDownloadList #import enigma2_api TMPDIR = "/tmp/playstream/" e2 = None 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 ="&".jpin(headers) url = url+"#"+headers print url url = url.encode("utf8") service = eServiceReference(4097, 0, url) service.setName( stream["name"]) return service ##################################################################################################################### class TVPlayer(MoviePlayer): def __init__(self, session, service): MoviePlayer.__init__(self, session, service) self.skinName = "MoviePlayer" def leavePlayer(self): self.close() #self.session.openWithCallback(self.leavePlayerConfirmed, MessageBox, _("Stop playing?")) def leavePlayerConfirmed(self, answer): if answer: self.close() def doEofInternal(self, playing): self.close() #def getPluginList(self): #from Components.PluginComponent import plugins #list = [] #for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU): #if p.name != _("TV Play"): #list.append(((boundFunction(self.getPluginName, p.name), #boundFunction(self.runPlugin, p), lambda: True), None)) #return list #def showMovies(self): #pass ##################################################################################################################### class PSPlayer(TVPlayer): def __init__(self, session, streams): self.session = session self.streams = streams self.cur_stream = self.streams[0] # TODO self.selected = 0 service = make_service(self.cur_stream) TVPlayer.__init__(self, session, service) self["actions"] = ActionMap(["MoviePlayerActions","MovieSelectionActions","ColorActions","InfobarAudioSelectionActions"], { "leavePlayer":self.leavePlayer, "AudioSelection":self.select_stream, "audioSelection":self.select_stream, "green":self.select_stream, "showEventInfo":self.service_info }) self.skinName = "MoviePlayer" def play_service(self,service): self.movieSelected(service) def service_info(self): print "########[MoviePlayer] service_info" from Tools import Notifications text = "%s\n%s %s\n%s"%(self.cur_stream["name"],self.cur_stream["lang"],self.cur_stream["quality"],self.cur_stream["desc"]) text = text.encode("utf8") #print text type = MessageBox.TYPE_INFO Notifications.AddPopup(text = text, type=None, timeout = 10) #Notifications.MessageBox(self.session, text=text, type=MessageBox.TYPE_INFO, timeout=10, #close_on_any_key=True, #default=True, #enable_input=True, #msgBoxID=None, #picon=False, #simple=False, #wizard=False, #list=None, #skin_name=None, #timeout_default=None) #return True def select_stream(self): print "########[MoviePlayer] select_stream" lst = [] title = "Select stream" for i,s in enumerate(self.streams): lst.append(("[%s,%s] %s"%(s["lang"],s["quality"],s["name"]),i)) self.session.openWithCallback(self.cb_select_stream, ChoiceBox, text = title, list = lst,selection = self.selected) def cb_select_stream(self,answer): #print "item_menu_selected",answer if not answer: return self.selected = answer[1] service = make_service(self.streams[self.selected]) self.play_service(service) ##################################################################################################################### class MainScreen(Screen): skin = """ { "template": [MultiContentEntryText(pos=(10, 1), size=(560, 30), \ font=0, flags=RT_HALIGN_LEFT, text=0)], "fonts": [gFont("Regular", 20)], "itemHeight": 30 } """ def __init__(self, session): Screen.__init__(self, session) #self.setTitle2("Home") self.session = session self.e2 = None self["key_red"] = Button(_("Back")) self["key_green"] = Button(_("Select")) self["key_yellow"] = Button(_("Options")) self["key_menu"] = Button("Menu" ) self["actions"] = ActionMap(["OkCancelActions", "ColorActions","MenuActions", "NumberActions"], { "cancel": self.Cancel, "ok": self.Ok, "green": self.Ok, "red": self.Back, "yellow": self.options_screen, "blue": self.download_list, "menu": self.item_menu, }) self["list"] = List([]) self["list"].onSelectionChanged.append(self.SelectionChanged) self["pic"] = Pixmap() self["cur"] = Label() self["title"] = Label() self.downloading = 0 self.activeDownloads = 0 if not os.path.exists(TMPDIR): os.mkdir(TMPDIR) self.onLayoutFinish.append(self.LayoutFinish) def LayoutFinish(self): self.cur_directory = os.path.dirname(os.path.realpath(__file__)) self.defimage = LoadPixmap(os.path.join(self.cur_directory,"PlayStream.png")) #self.defimage = None #sc = AVSwitch().getFramebufferScale() #self.defimage0 = ePicLoad() #self.defimage0.PictureData.get().append(boundFunction(self.FinishDecodeDef, os.path.join(self.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.activeDownloads = 0 self.images = {} self.images_url = {} self.picloads = {} self.history = [] reload(ContentSources) self.sources = ContentSources.ContentSources(os.path.join(self.cur_directory,"sources")) self.config = self.sources.plugins["config"] self.cur_menu = ("Home","config::home","","Sākums") # try: self.content = self.sources.get_content(self.cur_menu[1]) except Exception,e: self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO) return print self.content self["list"].setList(self.content) self["cur"].setText(self.content[0][3]) self.setTitle2(self.cur_menu[0]) self.ShowPic(self.content[0][2]) def SelectionChanged(self): current = self["list"].getCurrent() print "[PlayStream] SelectionChanged: current=",current if not current: return self["cur"].setText(current[3]) if current[3] else self["cur"].setText("") if current[2]: self.ShowPic(current[2]) else: self.ShowDefPic() def setTitle2(self,title): #print self.keys() self["title"].setText(title) def ShowDefPic(self): if self.defimage: self["pic"].instance.setPixmap(self.defimage) def ShowPic(self,image_url): if image_url == "default": self.ShowDefPic() return elif self.images.has_key(image_url): if self.images[image_url] in (-1,-2): return elif self.images[image_url] == -3: self.ShowDefPic() 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(TMPDIR, fname) self.download_image(image_path, image_url) else: # local file image_path = os.path.join(self.cur_directory,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() self.picloads[image_path].PictureData.get().append(boundFunction(self.FinishDecode, image_path)) self.picloads[image_path].setPara((self["pic"].instance.size().width(), self["pic"].instance.size().height(), sc[0], sc[1], False, 0, "#00000000")) print image_path,image_url self.picloads[image_path].startDecode(image_path) def FinishDecode(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).addCallback(boundFunction(self.downloadFinished, image_path,image_url)).addErrback(boundFunction(self.downloadFailed, image_path,image_url)) def downloadFinished(self, image_path, image_url, result): self.downloading -= 1 print "[TV Play] Image downloaded finished ",self.downloading,image_path, image_url,result self.start_decode(image_path,image_url) def downloadFailed(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 #if len(current[1].split("::"))<2 or not current[1].split("::")[1]: # # return # TODO - kaut kā jēdzigak jāapstradā nederigs ieraksts (paziņojums?) if self.sources.is_video(current[1]): if self.sources.stream_type(current[1]): stream = stream0 stream["url"] = cur2[1] stream["name"] = cur2[0] streams = [stream] else: try: streams = self.sources.get_streams(current[1]) except Exception,e: self.msg("Error - %s"%str(e)) return if streams: print streams self.session.open(PSPlayer, streams) else: self.msg("No stream found - %s"%(self.current[1])) return #if not self.sources.stream_type(current[1]): #current = self.sources.get_content(current[1]) #print "Item to play",current #stream_type = self.sources.stream_type(current[1]) #url = current[1] #title = current[0] #if not stream_type: # #print stream_type #self.msg("No stream found - %s"%(self.current[1])) #return #self.playVideo(current) elif current[1] == "back": cur_menu_old = self.cur_menu self.cur_menu = self.history.pop() 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.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.get_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.get_content(current) def get_content(self,current): self.history.append(self.cur_menu) self.cur_menu = current try: new_content = self.sources.get_content(self.cur_menu[1]) except Exception,e: self.cur_menu = self.history.pop() self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO) return self.setTitle2(self.cur_menu[0]) self.show_content(new_content) def Back(self): self["list"].setIndex(0) self.Ok() def show_content(self,content,index=0): self["list"].setList(content) self["list"].setIndex(index) self.SelectionChanged() def Cancel(self): #if os.path.exists(TMPDIR): #for name in os.listdir(TMPDIR): #os.remove(os.path.join(TMPDIR, name)) #os.rmdir(TMPDIR) self.close() def playVideo(self, current): hlspath = current[1] print "[TV Play] Play:", current ##SERVICE 4097:0:0:0:0:0:0:0:0:0:http%3a//127.0.0.1%3a7777#Cookie1=test3&User-Agent=CustomUserAgent&Cookie=test4:test_headers ref = eServiceReference(4097, 0, hlspath) ref.setName(current[0]) self.session.open(TVPlayer, ref) 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"), ("Download list","download_list","Show download list") ] if self.sources.is_video(self.current[1]): lst.extend([ ("Download video","download","Download video in background"), ]) 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"), ]) title = self.current[0] self.session.openWithCallback(self.cb_item_menu, ChoiceBox, text = title, list = lst) #TODO def cb_item_menu(self,answer): #print "item_menu_selected",answer if not answer: return if answer[1] == "info": self.session.open(MessageBox, "Not yet implemented!", MessageBox.TYPE_INFO) pass # TODO parada papildus 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 = [(l,l) for l in lists] self.session.openWithCallback(self.cb_favorites, ChoiceBox, text="Select menu item will be added",list = lists2) elif answer[1] == 'download': current = self.current if not self.sources.is_video(current[1]): self.msg("Can not download listst (yet) - %s"%(current[1])) return #if not self.sources.stream_type(current[1]): #current = self.sources.get_content(current[1]) try: streams = self.sources.get_streams(current[1]) except Exception,e: self.msg("Error - %s"%str(e)) return if not streams: self.msg("No stream found to download - %s"%(self.current[1])) return if len(streams)>1: stream = streams[0] # TODO iespeja izvelēties strīmu, ja to ir vairāki else: stream = streams[0] stream_type = stream["type"] #self.sources.stream_type(stream["url"]) if not stream_type: # self.msg("Not supported stream type found to download - %s"%(self.current[1])) return title = streams[0]["name"] url = streams[0]["url"] downloadDir = "/media/hdd/movie" #config.plugins.playstream.downloadDir.value TODO if not os.path.exists(downloadDir): self.msg(_('Sorry, download directory "%s" not exist!\nPlease specify in the settings existing directory'%downloadDir)) return fname = re.sub("[/\n\r\t,]","_",title)+".mp4" outputfile = os.path.join(downloadDir, fname) if os.path.exists(outputfile): self.msg( _('Sorry, this file already exists:\n%s') % outputfile) return print "Trying to download - ", current if stream_type in ("http","https"): print "\n**Download %s - %s"%(title,url) #reload(downloadJob) job_manager.AddJob(downloadJob(url, outputfile, title[:20], self.video_download_stop)) self.activeDownloads += 1 self.msg(_('Video download started!')) 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.activeDownloads += 1 self.msg(_('Video download started!')) elif stream_type == "rstp": self.msg("RSTP stream download not yet implemented!") else: self.msg("Unkown stream type!") elif answer[1] == 'download_list': self.download_list() elif answer[1] == "delete": lst = self.cur_menu[1].replace("config::","") #print lst self.config.del_item(lst,self.index) 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 return def download_list(self): self.session.open(VideoDownloadList) 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() 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 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 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.activeDownloads -= 1 #self.msg("Download '%s'finished!"%title) print "video_download_stop ", title def msg(self,msg,timeout=10): self.session.open(MessageBox, msg, MessageBox.TYPE_INFO, timeout) ########################################################################## from Components.config import config, ConfigSubsection, ConfigYesNo,\ getConfigListEntry, ConfigSelection, ConfigNumber, ConfigDirectory,ConfigText, ConfigSubDict from Components.ConfigList import ConfigListScreen #from Screens.LocationBox import LocationBox config.plugins.playstream = ConfigSubDict() class OptionsScreen(ConfigListScreen,Screen): skin = """ """ def __init__(self, session,*args): self.session = session Screen.__init__(self, session) self.main = args[0] self.setTitle(self.main.cur_menu[0]+" Options") self.source = self.main.cur_menu[1].split("::")[0] self.cfg = 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["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) 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)