1 from __future__ import print_function
4 from Plugins.Plugin import PluginDescriptor
6 from Components.ActionMap import HelpableActionMap
7 from Components.ChoiceList import ChoiceEntryComponent
8 from Components.config import config, ConfigSubsection, ConfigEnableDisable
9 from Components.SystemInfo import SystemInfo
10 from Components.ParentalControl import parentalControl
11 from enigma import ePoint, eServiceReference, getDesktop
12 from Screens.ChannelSelection import ChannelContextMenu, ChannelSelection, ChannelSelectionBase
13 from Screens.InfoBar import InfoBar, MoviePlayer
14 from Screens.InfoBarGenerics import InfoBarNumberZap, InfoBarEPG, InfoBarChannelSelection, InfoBarPiP, InfoBarShowMovies, InfoBarTimeshift, InfoBarSeek, InfoBarPlugins
15 from Screens.PictureInPicture import PictureInPicture
16 from Screens.Screen import Screen
17 from Screens.MessageBox import MessageBox
18 from PipzapSetup import PipzapSetup
19 from Components.PluginComponent import plugins
25 #pragma mark ChannelSelection
28 # ChannelContextMenu: switch "Activate Picture in Picture" for pip/mainpicture
29 def ChannelContextMenu___init__(self, session, csel, *args, **kwargs):
30 baseMethods.ChannelContextMenu__init__(self, session, csel, *args, **kwargs)
33 list = self["menu"].list
35 # TRANSLATORS: Do NOT translate this! This is not a string in our plugin but one from e2 core which we try to find, so a custom translation will probably disallow us to do so.
36 searchText = _("Activate Picture in Picture")
38 if entry[0][0] == searchText:
40 entry = ChoiceEntryComponent("", (_("play in mainwindow"), self.playMain))
42 entry = ChoiceEntryComponent("blue", (_("play as picture in picture"), self.showServiceInPiP))
46 self["menu"].setList(list)
48 def ChannelContextMenu_playMain(self):
49 # XXX: we want to keep the current selection
50 sel = self.csel.getCurrentSelection()
52 self.csel.setCurrentSelection(sel)
55 # do not hide existing pip
56 def ChannelContextMenu_showServiceInPiP(self):
57 if not self.pipAvailable:
60 if not self.session.pipshown:
61 self.session.pip = self.session.instantiateDialog(PictureInPicture)
62 self.session.pip.show()
64 newservice = self.csel.servicelist.getCurrent()
65 if self.session.pip.playService(newservice):
66 self.session.pipshown = True
67 self.session.pip.servicePath = self.csel.getCurrentServicePath()
70 self.session.pipshown = False
72 self.session.openWithCallback(self.close, MessageBox, _("Could not open Picture in Picture"), MessageBox.TYPE_ERROR)
74 def ChannelSelectionBase__init__(self, *args, **kwargs):
75 baseMethods.ChannelSelectionBase__init__(self, *args, **kwargs)
77 self.enable_pipzap = False
79 def ChannelSelectionBase_setCurrentSelection(self, service, *args, **kwargs):
81 baseMethods.ChannelSelectionBase_setCurrentSelection(self, service, *args, **kwargs)
83 def ChannelSelection_channelSelected(self, *args, **kwargs):
84 self.enable_pipzap = True
85 baseMethods.ChannelSelection_channelSelected(self, *args, **kwargs)
87 def ChannelSelection_togglePipzap(self):
88 assert(self.session.pip)
89 title = self.instance.getTitle()
90 pos = title.find(" (")
94 # Mark PiP as inactive and effectively deactivate pipzap
95 self.session.pip.inactive()
98 # Disable PiP if not playing a service
99 if self.session.pip.pipservice is None:
100 self.session.pipshown = False
103 # Move to playing service
104 lastservice = eServiceReference(self.lastservice.value)
105 if lastservice.valid() and self.getCurrentSelection() != lastservice:
106 self.setCurrentSelection(lastservice)
110 # Mark PiP as active and effectively active pipzap
111 self.session.pip.active()
114 # Move to service playing in pip (will not work with subservices)
115 self.setCurrentSelection(self.session.pip.getCurrentService())
119 self.buildTitleString()
121 def ChannelSelection_zap(self, *args, **kwargs):
122 if self.enable_pipzap and self.dopipzap:
123 if not self.session.pipshown:
124 self.session.pip = self.session.instantiateDialog(PictureInPicture)
125 self.session.pip.show()
126 self.session.pipshown = True
128 ref = self.session.pip.getCurrentService()
129 nref = self.getCurrentSelection()
130 if ref is None or ref != nref:
131 if not config.ParentalControl.configured.value or parentalControl.getProtectionLevel(nref.toCompareString()) == -1:
132 if not self.session.pip.playService(nref):
133 # XXX: Make sure we set an invalid ref
134 self.session.pip.playService(None)
136 baseMethods.ChannelSelection_zap(self, *args, **kwargs)
138 # Yes, we might double-check this, but we need to re-select pipservice if pipzap is active
139 # and we just wanted to zap in mainwindow once
140 # XXX: do we really want this? this also resets the service when zapping from context menu
141 # which is irritating
143 # This unfortunately won't work with subservices
144 self.setCurrentSelection(self.session.pip.getCurrentService())
145 self.enable_pipzap = False
147 def ChannelSelection_setHistoryPath(self, *args, **kwargs):
148 baseMethods.ChannelSelection_setHistoryPath(self, *args, **kwargs)
150 self.setCurrentSelection(self.session.pip.getCurrentService())
152 def ChannelSelection_cancel(self, *args, **kwargs):
153 if self.revertMode is None and self.dopipzap:
154 # This unfortunately won't work with subservices
155 self.setCurrentSelection(self.session.pip.getCurrentService())
156 self.revertMode = 1337 # not in (None, MODE_TV, MODE_RADIO)
157 baseMethods.ChannelSelection_cancel(self, *args, **kwargs)
160 #pragma mark MoviePlayer
163 def MoviePlayer__init__(self, *args, **kwargs):
164 baseMethods.MoviePlayer__init__(self, *args, **kwargs)
165 self.servicelist = InfoBar.instance and InfoBar.instance.servicelist
167 self["DirectionActions"] = HelpableActionMap(self, "DirectionActions",
173 def MoviePlayer_up(self):
174 slist = self.servicelist
175 if slist and slist.dopipzap:
177 self.session.execDialog(slist)
181 def MoviePlayer_down(self):
182 slist = self.servicelist
183 if slist and slist.dopipzap:
185 self.session.execDialog(slist)
189 def MoviePlayer_right(self):
190 # XXX: gross hack, we do not really seek if changing channel in pip :-)
191 slist = self.servicelist
192 if slist and slist.dopipzap:
193 # XXX: We replicate InfoBarChannelSelection.zapDown here - we shouldn't do that
194 if slist.inBouquet():
195 prev = slist.getCurrentSelection()
197 prev = prev.toString()
199 if config.usage.quickzap_bouquet_change.value and slist.atEnd():
203 cur = slist.getCurrentSelection()
204 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
208 slist.enable_pipzap = True
211 InfoBarSeek.seekFwd(self)
213 def MoviePlayer_left(self):
214 slist = self.servicelist
215 if slist and slist.dopipzap:
216 # XXX: We replicate InfoBarChannelSelection.zapUp here - we shouldn't do that
217 if slist.inBouquet():
218 prev = slist.getCurrentSelection()
220 prev = prev.toString()
222 if config.usage.quickzap_bouquet_change.value:
226 cur = slist.getCurrentSelection()
227 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
231 slist.enable_pipzap = True
234 InfoBarSeek.seekBack(self)
236 def MoviePlayer_swapPiP(self):
240 #pragma mark InfoBarGenerics
243 def InfoBarNumberZap_zapToNumber(self, *args, **kwargs):
245 self.servicelist.enable_pipzap = True
246 except AttributeError as ae:
248 baseMethods.InfoBarNumberZap_zapToNumber(self, *args, **kwargs)
250 def InfoBarChannelSelection_zapUp(self, *args, **kwargs):
251 self.servicelist.enable_pipzap = True
252 baseMethods.InfoBarChannelSelection_zapUp(self, *args, **kwargs)
254 def InfoBarChannelSelection_zapDown(self, *args, **kwargs):
255 self.servicelist.enable_pipzap = True
256 baseMethods.InfoBarChannelSelection_zapDown(self, *args, **kwargs)
258 def InfoBarEPG_zapToService(self, *args, **kwargs):
260 self.servicelist.enable_pipzap = True
261 except AttributeError as ae:
263 baseMethods.InfoBarEPG_zapToService(self, *args, **kwargs)
265 def InfoBarShowMovies__init__(self):
266 baseMethods.InfoBarShowMovies__init__(self)
267 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
269 "movieList": (self.showMovies, _("movie list")),
270 "up": (self.up, _("movie list")),
271 "down": (self.down, _("movie list"))
274 def InfoBarPiP__init__(self):
275 baseMethods.InfoBarPiP__init__(self)
276 if SystemInfo.get("NumVideoDecoders", 1) > 1 and self.allowPiP:
277 self.addExtension((self.getTogglePipzapName, self.togglePipzap, self.pipzapAvailable), "red")
278 if config.plugins.pipzap.enable_hotkey.value:
279 self["pipzapActions"] = HelpableActionMap(self, "pipzapActions",
281 "switchPiP": (self.togglePipzap, _("zap in pip window...")),
284 def InfoBarPiP_pipzapAvailable(self):
286 return True if self.servicelist and self.session.pipshown else False
287 except AttributeError as ae:
290 def InfoBarPiP_getTogglePipzapName(self):
291 slist = self.servicelist
292 if slist and slist.dopipzap:
293 return _("Zap focus to main screen")
294 return _("Zap focus to Picture in Picture")
296 def InfoBarPiP_togglePipzap(self):
297 # supposed to fix some problems with permanent timeshift patch
298 if isinstance(self, InfoBarTimeshift) and isinstance(self, InfoBarSeek) and \
299 self.timeshift_enabled and self.isSeekable():
302 if not self.session.pipshown:
304 slist = self.servicelist
308 def InfoBarPiP_togglePipzapHelpable(self):
309 """Stupid helper for InfoBarPiP_togglePipzap to optimize away the check if help should be shown if it already was."""
310 InfoBarPiP.togglePipzap = InfoBarPiP_togglePipzap
312 if config.plugins.pipzap.show_help.value and pipzapHelp:
313 pipzapHelp.open(self.session)
314 config.plugins.pipzap.show_help.value = False
315 config.plugins.pipzap.save()
319 def InfoBarPiP_showPiP(self, *args, **kwargs):
321 slist = self.servicelist
322 except AttributeError as ae:
325 if self.session.pipshown and slist and slist.dopipzap:
327 elif not self.session.pipshown and isinstance(self, InfoBarShowMovies):
328 self.session.pip = self.session.instantiateDialog(PictureInPicture)
329 self.session.pip.show()
330 self.session.pipshown = True
332 self.session.pip.playService(slist.getCurrentSelection())
334 baseMethods.InfoBarPiP_showPiP(self, *args, **kwargs)
336 # Using the base implementation would cause nasty bugs, so ignore it here
337 def InfoBarPiP_swapPiP(self):
338 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
339 pipref = self.session.pip.getCurrentService()
340 if pipref and swapservice and pipref.toString() != swapservice.toString():
341 self.session.pip.playService(swapservice)
344 slist = self.servicelist
345 except AttributeError as ae:
348 # TODO: this behaves real bad on subservices
350 slist.servicelist.setCurrent(swapservice)
352 slist.servicelist.setCurrent(pipref)
354 slist.addToHistory(pipref) # add service to history
355 slist.lastservice.value = pipref.toString() # save service as last playing one
356 self.session.nav.stopService() # stop portal
357 self.session.nav.playService(pipref) # start subservice
360 #pragma mark Picture in Picture
363 class PictureInPictureZapping(Screen):
364 skin = """<screen name="PictureInPictureZapping" flags="wfNoBorder" position="50,50" size="90,26" title="PiPZap" zPosition="-1">
365 <eLabel text="PiP-Zap" position="0,0" size="90,26" foregroundColor="#00ff66" font="Regular;26" />
367 def refreshPosition(self):
368 x = config.av.pip.value[0]
369 y = config.av.pip.value[1]
370 width = getDesktop(0).size().width()
371 height = getDesktop(0).size().height()
380 self.instance.move(ePoint(x, y))
382 def PictureInPicture__init__(self, session, *args, **kwargs):
383 baseMethods.PictureInPicture__init__(self, session, *args, **kwargs)
384 self.pipActive = session.instantiateDialog(PictureInPictureZapping)
386 def PictureInPicture_active(self):
387 if config.plugins.pipzap.show_label.value:
388 self.pipActive.show()
390 def PictureInPicture_inactive(self):
391 self.pipActive.hide()
393 def PictureInPicture_move(self, *args, **kwargs):
394 baseMethods.PictureInPicture_move(self, *args, **kwargs)
395 self.pipActive.refreshPosition()
402 if SystemInfo.get("NumVideoDecoders", 1) > 1:
403 from Plugins.SystemPlugins.MPHelp import registerHelp, XMLHelpReader
404 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
405 reader = XMLHelpReader(resolveFilename(SCOPE_PLUGINS, "Extensions/pipzap/mphelp.xml"))
406 pipzapHelp = registerHelp(*reader)
407 except Exception as e:
408 print("[pipzap] Unable to initialize MPHelp:", e,"- Help not available!")
415 def overwriteFunctions():
416 """Overwrite existing functions here to increase system stability a bit."""
418 baseMethods.ChannelContextMenu__init__
419 except AttributeError as ae:
422 print("[pipzap] already initialized, aborting.")
425 global ChannelContextMenu, ChannelSelection, ChannelSelectionBase
427 from Plugins.Extensions.AdvancedChannelSelection import plugin
428 except ImportError as ie:
431 if config.plugins.AdvancedChannelSelection.enabled.value:
432 print("[pipzap] ACS is installed and activated, ugly just invited scary to the party xD")
433 from Plugins.Extensions.AdvancedChannelSelection.ChannelSelection import ChannelContextMenu, ChannelSelection, ChannelSelectionBase
435 baseMethods.ChannelContextMenu__init__ = ChannelContextMenu.__init__
436 ChannelContextMenu.__init__ = ChannelContextMenu___init__
438 ChannelContextMenu.playMain = ChannelContextMenu_playMain
439 ChannelContextMenu.showServiceInPiP = ChannelContextMenu_showServiceInPiP
441 baseMethods.ChannelSelectionBase__init__ = ChannelSelectionBase.__init__
442 ChannelSelectionBase.__init__ = ChannelSelectionBase__init__
444 baseMethods.ChannelSelectionBase_setCurrentSelection = ChannelSelectionBase.setCurrentSelection
445 ChannelSelectionBase.setCurrentSelection = ChannelSelectionBase_setCurrentSelection
447 baseMethods.ChannelSelection_channelSelected = ChannelSelection.channelSelected
448 ChannelSelection.channelSelected = ChannelSelection_channelSelected
450 ChannelSelection.togglePipzap = ChannelSelection_togglePipzap
452 baseMethods.ChannelSelection_zap = ChannelSelection.zap
453 ChannelSelection.zap = ChannelSelection_zap
455 baseMethods.ChannelSelection_setHistoryPath = ChannelSelection.setHistoryPath
456 ChannelSelection.setHistoryPath = ChannelSelection_setHistoryPath
458 baseMethods.ChannelSelection_cancel = ChannelSelection.cancel
459 ChannelSelection.cancel = ChannelSelection_cancel
461 baseMethods.MoviePlayer__init__ = MoviePlayer.__init__
462 MoviePlayer.__init__ = MoviePlayer__init__
464 MoviePlayer.allowPiP = property(lambda *x: True, lambda *x: None)
466 MoviePlayer.up = MoviePlayer_up
467 MoviePlayer.down = MoviePlayer_down
468 MoviePlayer.right = MoviePlayer_right
469 MoviePlayer.left = MoviePlayer_left
470 MoviePlayer.swapPiP = MoviePlayer_swapPiP
472 baseMethods.InfoBarNumberZap_zapToNumber = InfoBarNumberZap.zapToNumber
473 InfoBarNumberZap.zapToNumber = InfoBarNumberZap_zapToNumber
475 baseMethods.InfoBarChannelSelection_zapUp = InfoBarChannelSelection.zapUp
476 InfoBarChannelSelection.zapUp = InfoBarChannelSelection_zapUp
478 baseMethods.InfoBarChannelSelection_zapDown = InfoBarChannelSelection.zapDown
479 InfoBarChannelSelection.zapDown = InfoBarChannelSelection_zapDown
481 baseMethods.InfoBarEPG_zapToService = InfoBarEPG.zapToService
482 InfoBarEPG.zapToService = InfoBarEPG_zapToService
484 baseMethods.InfoBarShowMovies__init__ = InfoBarShowMovies.__init__
485 InfoBarShowMovies.__init__ = InfoBarShowMovies__init__
487 baseMethods.InfoBarPiP__init__ = InfoBarPiP.__init__
488 InfoBarPiP.__init__ = InfoBarPiP__init__
490 InfoBarPiP.pipzapAvailable = InfoBarPiP_pipzapAvailable
491 InfoBarPiP.getTogglePipzapName = InfoBarPiP_getTogglePipzapName
492 InfoBarPiP.swapPiP = InfoBarPiP_swapPiP
494 if config.plugins.pipzap.show_help.value and pipzapHelp:
495 InfoBarPiP.togglePipzap = InfoBarPiP_togglePipzapHelpable
497 InfoBarPiP.togglePipzap = InfoBarPiP_togglePipzap
499 baseMethods.InfoBarPiP_showPiP = InfoBarPiP.showPiP
500 InfoBarPiP.showPiP = InfoBarPiP_showPiP
502 baseMethods.PictureInPicture__init__ = PictureInPicture.__init__
503 PictureInPicture.__init__ = PictureInPicture__init__
505 PictureInPicture.active = PictureInPicture_active
506 PictureInPicture.inactive = PictureInPicture_inactive
508 baseMethods.PictureInPicture_move = PictureInPicture.move
509 PictureInPicture.move = PictureInPicture_move
511 config.plugins.pipzap = ConfigSubsection()
512 config.plugins.pipzap.enable_hotkey = ConfigEnableDisable(default = True)
513 config.plugins.pipzap.show_in_plugins = ConfigEnableDisable(default = False)
514 config.plugins.pipzap.show_label = ConfigEnableDisable(default = True)
515 config.plugins.pipzap.show_help = ConfigEnableDisable(default = True)
517 def autostart(reason, **kwargs):
521 def activate(session, *args, **kwargs):
522 infobar = InfoBar.instance
524 session.open(MessageBox, _("Unable to access InfoBar.\npipzap not available."), MessageBox.TYPE_ERROR)
525 elif hasattr(infobar, 'togglePipzap'): # check if plugin is already hooked into enigma2
526 infobar.togglePipzap()
528 session.open(MessageBox, _("pipzap not properly installed.\nPlease restart Enigma2."), MessageBox.TYPE_ERROR)
530 def main(session, *args, **kwargs):
531 session.open(PipzapSetup)
534 if menuid != "system":
536 return [(_("pipzap"), main, "pipzap_setup", None)]
538 def housekeepingPluginmenu(el):
540 plugins.addPlugin(activateDescriptor)
542 plugins.removePlugin(activateDescriptor)
544 config.plugins.pipzap.show_in_plugins.addNotifier(housekeepingPluginmenu, initial_call=False, immediate_feedback=True)
545 activateDescriptor = PluginDescriptor(name="pipzap", description=_("Toggle pipzap status"), where=PluginDescriptor.WHERE_PLUGINMENU, fnc=activate, needsRestart=False)
547 def showHideNotifier(el):
548 infobar = InfoBar.instance
551 session = infobar.session
552 slist = infobar.servicelist
553 if slist and hasattr(slist, 'dopipzap'): # check if plugin is already hooked into enigma2
555 if el.value and slist.dopipzap:
558 session.pip.inactive()
560 config.plugins.pipzap.show_label.addNotifier(showHideNotifier, initial_call=False, immediate_feedback=True)
562 def Plugins(**kwargs):
563 # do not add any entry if only one (or less :P) video decoders present
564 if SystemInfo.get("NumVideoDecoders", 1) < 2:
569 where=PluginDescriptor.WHERE_AUTOSTART,
571 needsRestart=True, # XXX: force restart for now as I don't think the plugin will work properly without one
574 where=PluginDescriptor.WHERE_MENU,
579 if config.plugins.pipzap.show_in_plugins.value:
580 l.append(activateDescriptor)