123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200 |
- #!/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)
- #
-
- __version__ = "0.6t"
- __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
-
- from plugin_locale import _
- import ContentSources
- import util
- from VideoDownload import DownloadJob, HLSDownloadJob,VideoDownloadList
- from 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)
-
- #####################################################################################################################
- 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)
- self.sources = ContentSources.ContentSources(os.path.join(cur_directory,"sources"))
- 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
- 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,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.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).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
-
- elif 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()
- 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.get_content(current)
-
-
- 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 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()
- 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 back(self):
- self["list"].setIndex(0)
- self.ok()
-
- def show_content(self,content,index=0):
- self["list"].setList(content)
- self["list"].setIndex(index)
- self.selection_changed()
-
- 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"),
- ("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"),
- ])
- 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, 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] 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)
-
- 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_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]))
- 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()
- 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 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 = """
- <screen position="%d,%d" size="%d,140" zPosition="2" backgroundColor="transparent" flags="wfNoBorder">
- <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" />
- </screen>""" % (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 <sc["begin"]:
- return " "
- else:
- txt = sc["text"] if sc["text"] else " "
- return txt
-
- def get_sub_ind(self,ind):
- subs_object = self.get_subs_current() # current subs object
- if subs_object:
- return subs_object.subs[ind] if ind<len(subs_object.subs) else None
- else:
- return None
-
- def get_subs_current(self):
- "Return current sub_object"
- if not "subs" in self.subs[self.cur_subs]:
- print "===== Captions to download", self.subs[self.cur_subs]["url"]
- subs_object = util.Captions(self.subs[self.cur_subs]["url"])
- print len(subs_object.subs), "items"
- self.subs[self.cur_subs]["subs"] = subs_object
- if not subs_object:
- return None
- else:
- return subs_object
- else:
- return self.subs[self.cur_subs]["subs"]
-
- def update_subtitles(self):
- if not self.shown and self.svisible:
- seek = self.getSeek()
- pos = seek.getPlayPosition()
- pts = pos[1]/90
- txt0 = "%d:%02d (%i)" % (pts/60/1000, (pts/1000)%60, pts)
- #print "Update_subtitles", txt0
- if not self.subtitle_window.shown:
- self.subtitle_window.show()
- if not self.subs:
- return
- txt = self.get_sub_pts(pts)
- #print "Show subtitle",txt
- #txt = txt0+": "+ txt
- self.subtitle_window["subtitle"].setText(txt)
- elif self.shown and self.svisible:
- if self.subtitle_window.shown:
- self.subtitle_window.hide()
- elif not self.svisible:
- if self.subtitle_window.shown:
- self.subtitle_window.hide()
-
- # struct SubtitleTrack
- # int type;
- # int pid;
- # int page_number;
- # int magazine_number;
- # std::string language_code;
- #selectedSubtitle = ???
- #self.enableSubtitle(selectedSubtitle)
-
- def play_service(self,service):
- self.movieSelected(service)
-
- #def doShow(self):
- #self.svisible = False
- #InfoBarShowHide.doShow(self)
-
- #def doHide(self):
- #self.svisible = True
- #InfoBarShowHide.doHide(self)
-
- def service_info(self):
- print "########[MoviePlayer] service_info"
- 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
- #mtype = MessageBox.TYPE_INFO
- #Notifications.AddPopup(text = text, type=mtype, timeout = 10)
- print "StreamInfo", self.cur_stream
- 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]}}
- self.session.open(StreamInfo, nfo)
-
- 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, title = 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.resume_pos = self.getSeek().getPlayPosition()[1]
- self.play_service(service)
-
- def serviceStarted(self):
- print "serviceStarted"
- if not self.resume_pos:
- self.resume_pos = 0
- print "doSeek",self.resume_pos
- #self.doSeek(self.resume_pos)
-
-
- def select_captions(self):
- print "########[MoviePlayer] select_caption"
- lst = []
- title = "Select subtitles"
- for i,s in enumerate(self.subs):
- lst.append((("%s - %s"%(s["lang"],s["name"])).encode("utf8"),i))
- if self.svisible:
- selection = self.cur_subs
- else:
- selection = len(lst)
- lst.append(("No captions",-1))
- self.session.openWithCallback(self.cb_select_captions, ChoiceBox, title = title, list = lst,selection = selection)
-
- def cb_select_captions(self,answer):
- #print "item_menu_selected",answer
- if not answer:
- return
- if answer[1] == -1:
- self.svisible = False
- else:
- self.cur_subs = answer[1]
- self.svisible = True
-
- 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 ConfigScreen(ConfigListScreen,Screen):
-
- def __init__(self, session, args = 0):
- self.session = session
- #self.setup_title = "Options"
- Screen.__init__(self, session)
- self.skinName = ["PSConfig"]
- self.list = [
- getConfigListEntry(_("Download folder"), config.plugins.playstream.download_dir),
- getConfigListEntry(_("Overwrite download video"), config.plugins.playstream.overwrite_download),
- getConfigListEntry(_("TMP folder"), config.plugins.playstream.tmp_dir),
- getConfigListEntry(_("Clear tmp folder on exit"), config.plugins.playstream.clear_tmp),
- getConfigListEntry(_("Start playstreamproxy"), config.plugins.playstream.streamproxy_start),
- getConfigListEntry(_("Check new plugin version in feed"), config.plugins.playstream.check_update)
- ]
- 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("Update")
- self["key_blue"] = Button("Downloads")
- self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
- {
- "red": self.cancel,
- "green": self.save,
- "yellow": self.update_plugin,
- "blue": self.downloads,
- "save": self.save,
- "cancel": self.cancel,
- "ok": self.ok,
- }, -2)
- self["title"].setText("Version %s, ivars777@gmail.com"%__version__)
-
- def getCurrentEntry(self):
- return self["config"].getCurrent()[0]
-
- def getCurrentValue(self):
- return str(self["config"].getCurrent()[1].getText())
-
- def ok(self):
- if self["config"].getCurrent()[1] == config.plugins.playstream.download_dir:
- folder = config.plugins.playstream.download_dir.value
- #self.session.openWithCallback(self.cb_download_dir, LocationBox,"Select Folder")
- 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)
- elif self["config"].getCurrent()[1] == config.plugins.playstream.tmp_dir:
- 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)
- else:
- self.save()
-
- def cb_download_dir(self, folder, select=None):
- if not folder:
- return
- print "Folder selected - %s"%folder
- config.plugins.playstream.download_dir.setValue(folder)
- config.plugins.playstream.download_dir.save()
- config.plugins.playstream.locations.save()
- config.save()
-
- def select_tmp_dir(self, folder, select=None):
- if not folder:
- return
- print "Folder selected - %s"%folder
- config.plugins.playstream.tmp_dir.setValue(folder)
- config.plugins.playstream.tmp_dir.save()
- config.plugins.playstream.locations.save()
- config.save()
-
- def save(self):
- print "saving"
- self.saveAll()
- self.close(True,self.session)
-
- def cancel(self):
- #print "cancel"
- self.close(False,self.session)
-
- def downloads(self):
- self.session.open(VideoDownloadList)
-
- def update_plugin(self):
- #print "Check PlayStream for 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__)
- 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).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()
- self.picloads.PictureData.get().append(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()
|