1 # -*- coding: UTF-8 -*-
3 from Components.ActionMap import NumberActionMap
4 from Components.config import config, ConfigSubsection, ConfigInteger
5 from Components.Console import Console
6 from Components.Label import Label
7 from Components.Pixmap import Pixmap
8 from Components.VideoWindow import VideoWindow
9 from enigma import eServiceCenter, eServiceReference, eTimer, getDesktop, loadPNG
10 from os import environ
11 from Plugins.Plugin import PluginDescriptor
12 from Screens.ChannelSelection import BouquetSelector
13 from Screens.MessageBox import MessageBox
14 from Screens.Screen import Screen
15 from Tools.Directories import resolveFilename, SCOPE_SKIN_IMAGE, SCOPE_LANGUAGE, SCOPE_PLUGINS
16 from Tools.LoadPixmap import LoadPixmap
18 ################################################
20 grab_binary = "/usr/bin/grab"
21 grab_picture = "/tmp/mosaic.jpg"
22 grab_errorlog = "/tmp/mosaic.log"
24 config_limits = (3, 30)
25 config.plugins.Mosaic = ConfigSubsection()
26 config.plugins.Mosaic.countdown = ConfigInteger(default=5, limits=config_limits)
28 playingIcon = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/ico_mp_play.png'))
29 pausedIcon = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/ico_mp_pause.png'))
35 desktop = getDesktop(0)
38 height = size.height()
39 windowWidth = width / 4
40 windowHeight = height / 4
45 for i in range(1, 10):
46 positions.append([x, y])
48 x += ((width - 160) - (windowWidth * 3)) / 2
49 if (i == 3) or (i == 6):
50 y = y + windowHeight + 20
54 skin += """<screen position="0,0" size="%d,%d" title="Mosaic" flags="wfNoBorder" backgroundColor="#ffffff" >""" % (width, height)
55 skin += """<widget name="playState" position="55,55" size="16,16" alphatest="on" />"""
56 skin += """<eLabel position="%d,%d" size="%d,%d" />""" % (positions[0][0]-2, positions[0][1]-1, windowWidth, windowHeight)
57 skin += """<eLabel position="%d,%d" size="%d,%d" />""" % (positions[1][0]-2, positions[1][1]-1, windowWidth, windowHeight)
58 skin += """<eLabel position="%d,%d" size="%d,%d" />""" % (positions[2][0]-2, positions[2][1]-1, windowWidth, windowHeight)
59 skin += """<eLabel position="%d,%d" size="%d,%d" />""" % (positions[3][0]-2, positions[3][1]-1, windowWidth, windowHeight)
60 skin += """<eLabel position="%d,%d" size="%d,%d" />""" % (positions[4][0]-2, positions[4][1]-1, windowWidth, windowHeight)
61 skin += """<eLabel position="%d,%d" size="%d,%d" />""" % (positions[5][0]-2, positions[5][1]-1, windowWidth, windowHeight)
62 skin += """<eLabel position="%d,%d" size="%d,%d" />""" % (positions[6][0]-2, positions[6][1]-1, windowWidth, windowHeight)
63 skin += """<eLabel position="%d,%d" size="%d,%d" />""" % (positions[7][0]-2, positions[7][1]-1, windowWidth, windowHeight)
64 skin += """<eLabel position="%d,%d" size="%d,%d" />""" % (positions[8][0]-2, positions[8][1]-1, windowWidth, windowHeight)
65 skin += """<widget name="channel1" position="%d,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />""" % (positions[0][0], positions[0][1]-18, windowWidth-4)
66 skin += """<widget name="channel2" position="%d,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />""" % (positions[1][0], positions[1][1]-18, windowWidth-4)
67 skin += """<widget name="channel3" position="%d,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />""" % (positions[2][0], positions[2][1]-18, windowWidth-4)
68 skin += """<widget name="channel4" position="%d,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />""" % (positions[3][0], positions[3][1]-18, windowWidth-4)
69 skin += """<widget name="channel5" position="%d,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />""" % (positions[4][0], positions[4][1]-18, windowWidth-4)
70 skin += """<widget name="channel6" position="%d,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />""" % (positions[5][0], positions[5][1]-18, windowWidth-4)
71 skin += """<widget name="channel7" position="%d,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />""" % (positions[6][0], positions[6][1]-18, windowWidth-4)
72 skin += """<widget name="channel8" position="%d,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />""" % (positions[7][0], positions[7][1]-18, windowWidth-4)
73 skin += """<widget name="channel9" position="%d,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />""" % (positions[8][0], positions[8][1]-18, windowWidth-4)
74 skin += """<widget name="window1" position="%d,%d" zPosition="1" size="%d,%d" />""" % (positions[0][0]-2, positions[0][1]-1, windowWidth, windowHeight)
75 skin += """<widget name="window2" position="%d,%d" zPosition="1" size="%d,%d" />""" % (positions[1][0]-2, positions[1][1]-1, windowWidth, windowHeight)
76 skin += """<widget name="window3" position="%d,%d" zPosition="1" size="%d,%d" />""" % (positions[2][0]-2, positions[2][1]-1, windowWidth, windowHeight)
77 skin += """<widget name="window4" position="%d,%d" zPosition="1" size="%d,%d" />""" % (positions[3][0]-2, positions[3][1]-1, windowWidth, windowHeight)
78 skin += """<widget name="window5" position="%d,%d" zPosition="1" size="%d,%d" />""" % (positions[4][0]-2, positions[4][1]-1, windowWidth, windowHeight)
79 skin += """<widget name="window6" position="%d,%d" zPosition="1" size="%d,%d" />""" % (positions[5][0]-2, positions[5][1]-1, windowWidth, windowHeight)
80 skin += """<widget name="window7" position="%d,%d" zPosition="1" size="%d,%d" />""" % (positions[6][0]-2, positions[6][1]-1, windowWidth, windowHeight)
81 skin += """<widget name="window8" position="%d,%d" zPosition="1" size="%d,%d" />""" % (positions[7][0]-2, positions[7][1]-1, windowWidth, windowHeight)
82 skin += """<widget name="window9" position="%d,%d" zPosition="1" size="%d,%d" />""" % (positions[8][0]-2, positions[8][1]-1, windowWidth, windowHeight)
83 skin += """<widget name="video1" position="%d,%d" zPosition="2" size="%d,%d" backgroundColor="#ffffffff" />""" % (positions[0][0]-2, positions[0][1]-1, windowWidth, windowHeight)
84 skin += """<widget name="video2" position="%d,%d" zPosition="2" size="%d,%d" backgroundColor="#ffffffff" />""" % (positions[1][0]-2, positions[1][1]-1, windowWidth, windowHeight)
85 skin += """<widget name="video3" position="%d,%d" zPosition="2" size="%d,%d" backgroundColor="#ffffffff" />""" % (positions[2][0]-2, positions[2][1]-1, windowWidth, windowHeight)
86 skin += """<widget name="video4" position="%d,%d" zPosition="2" size="%d,%d" backgroundColor="#ffffffff" />""" % (positions[3][0]-2, positions[3][1]-1, windowWidth, windowHeight)
87 skin += """<widget name="video5" position="%d,%d" zPosition="2" size="%d,%d" backgroundColor="#ffffffff" />""" % (positions[4][0]-2, positions[4][1]-1, windowWidth, windowHeight)
88 skin += """<widget name="video6" position="%d,%d" zPosition="2" size="%d,%d" backgroundColor="#ffffffff" />""" % (positions[5][0]-2, positions[5][1]-1, windowWidth, windowHeight)
89 skin += """<widget name="video7" position="%d,%d" zPosition="2" size="%d,%d" backgroundColor="#ffffffff" />""" % (positions[6][0]-2, positions[6][1]-1, windowWidth, windowHeight)
90 skin += """<widget name="video8" position="%d,%d" zPosition="2" size="%d,%d" backgroundColor="#ffffffff" />""" % (positions[7][0]-2, positions[7][1]-1, windowWidth, windowHeight)
91 skin += """<widget name="video9" position="%d,%d" zPosition="2" size="%d,%d" backgroundColor="#ffffffff" />""" % (positions[8][0]-2, positions[8][1]-1, windowWidth, windowHeight)
92 skin += """<widget name="event1" position="%d,%d" size="%d,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />""" % (positions[0][0]-2, positions[0][1]-1, windowWidth)
93 skin += """<widget name="event2" position="%d,%d" size="%d,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />""" % (positions[1][0]-2, positions[1][1]-1, windowWidth)
94 skin += """<widget name="event3" position="%d,%d" size="%d,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />""" % (positions[2][0]-2, positions[2][1]-1, windowWidth)
95 skin += """<widget name="event4" position="%d,%d" size="%d,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />""" % (positions[3][0]-2, positions[3][1]-1, windowWidth)
96 skin += """<widget name="event5" position="%d,%d" size="%d,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />""" % (positions[4][0]-2, positions[4][1]-1, windowWidth)
97 skin += """<widget name="event6" position="%d,%d" size="%d,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />""" % (positions[5][0]-2, positions[5][1]-1, windowWidth)
98 skin += """<widget name="event7" position="%d,%d" size="%d,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />""" % (positions[6][0]-2, positions[6][1]-1, windowWidth)
99 skin += """<widget name="event8" position="%d,%d" size="%d,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />""" % (positions[7][0]-2, positions[7][1]-1, windowWidth)
100 skin += """<widget name="event9" position="%d,%d" size="%d,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />""" % (positions[8][0]-2, positions[8][1]-1, windowWidth)
102 skin += """<widget name="countdown" position="80,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />""" % (height-50, windowWidth)
103 skin += """<widget name="count" position="%d,%d" size="%d,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" halign="right" />
104 </screen>""" % (positions[2][0] ,height-50, windowWidth)
106 def __init__(self, session, services):
107 Screen.__init__(self, session)
109 self.session = session
110 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
112 self.Console = Console()
113 self.serviceHandler = eServiceCenter.getInstance()
114 self.ref_list = services
115 self.window_refs = [None, None, None, None, None, None, None, None, None]
116 self.current_refidx = 0
117 self.current_window = 1
118 self.countdown = config.plugins.Mosaic.countdown.value
120 self.state = self.PLAY
122 self["playState"] = Pixmap()
123 for i in range(1, 10):
124 self["window" + str(i)] = Pixmap()
125 self["video" + str(i)] = VideoWindow(decoder=0, fb_width=self.width, fb_height=self.height)
126 self["video" + str(i)].hide()
127 self["channel" + str(i)] = Label("")
128 self["event" + str(i)] = Label("")
129 self["event" + str(i)].hide()
130 self["video1"].decoder = 0
131 self["video1"].show()
132 self["countdown"] = Label()
133 self.updateCountdownLabel()
134 self["count"] = Label()
136 self["actions"] = NumberActionMap(["MosaicActions"],
139 "cancel": self.closeWithOldService,
141 "yellow": self.pause,
142 "channelup": self.countdownPlus,
143 "channeldown": self.countdownMinus,
144 "1": self.numberPressed,
145 "2": self.numberPressed,
146 "3": self.numberPressed,
147 "4": self.numberPressed,
148 "5": self.numberPressed,
149 "6": self.numberPressed,
150 "7": self.numberPressed,
151 "8": self.numberPressed,
152 "9": self.numberPressed
155 self.updateTimer = eTimer()
156 self.updateTimer_conn = self.updateTimer.timeout.connect(self.updateCountdown)
157 self.checkTimer = eTimer()
158 self.checkTimer_conn = self.checkTimer.timeout.connect(self.checkGrab)
159 self.checkTimer.start(500, 1)
162 # Start the first service in the bouquet and show the service-name
163 ref = self.ref_list[0]
164 self.window_refs[0] = ref
165 info = self.serviceHandler.info(ref)
166 name = info.getName(ref).replace('\xc2\x86', '').replace('\xc2\x87', '')
167 event_name = self.getEventName(info, ref)
168 self["channel1"].setText(name)
169 self["event1"].setText(event_name)
170 self.session.nav.playService(ref)
171 self["count"].setText(_("Channel: ") + "1 / " + str(len(self.ref_list)))
172 self["playState"].instance.setPixmap(playingIcon)
173 # Start updating the video-screenshots
174 self.updateTimer.start(1, 1)
176 def exit(self, callback=None):
177 self.deleteConsoleCallbacks()
180 def deleteConsoleCallbacks(self):
181 if self.Console.appContainers.has_key(self.consoleCmd):
182 del self.Console.appContainers[self.consoleCmd].dataAvail[:]
183 del self.Console.appContainers[self.consoleCmd].appClosed[:]
184 del self.Console.appContainers[self.consoleCmd]
185 del self.Console.extra_args[self.consoleCmd]
186 del self.Console.callbacks[self.consoleCmd]
188 def closeWithOldService(self):
189 self.session.nav.playService(self.oldService)
190 self.deleteConsoleCallbacks()
193 def numberPressed(self, number):
194 ref = self.window_refs[number-1]
196 self.session.nav.playService(ref)
197 self.deleteConsoleCallbacks()
201 if self.working == False and self.state == self.PAUSE:
202 self.state = self.PLAY
203 self.updateTimer.start(1000, 1)
204 self["playState"].instance.setPixmap(playingIcon)
207 if self.working == False and self.state == self.PLAY:
208 self.state = self.PAUSE
209 self.updateTimer.stop()
210 self["playState"].instance.setPixmap(pausedIcon)
212 def countdownPlus(self):
213 self.changeCountdown(1)
215 def countdownMinus(self):
216 self.changeCountdown(-1)
218 def changeCountdown(self, direction):
219 if self.working == False:
220 configNow = config.plugins.Mosaic.countdown.value
221 configNow += direction
223 if configNow < config_limits[0]:
224 configNow = config_limits[0]
225 elif configNow > config_limits[1]:
226 configNow = config_limits[1]
228 config.plugins.Mosaic.countdown.value = configNow
229 config.plugins.Mosaic.countdown.save()
231 self.updateCountdownLabel()
233 def makeNextScreenshot(self):
236 self.Console = Console()
237 self.consoleCmd = "%s -v -r %d -l -j 100 %s" % (grab_binary, self.windowWidth, grab_picture)
238 self.Console.ePopen(self.consoleCmd, self.showNextScreenshot)
240 def showNextScreenshot(self, result, retval, extra_args):
242 # Show screenshot in the current window
243 pic = LoadPixmap(grab_picture)
244 self["window" + str(self.current_window)].instance.setPixmap(pic)
246 # Hide current video-window and show the running event-name
247 self["video" + str(self.current_window)].hide()
248 self["event" + str(self.current_window)].show()
251 self.current_refidx += 1
252 if self.current_refidx > (len(self.ref_list) -1):
253 self.current_refidx = 0
256 ref = self.ref_list[self.current_refidx]
257 info = self.serviceHandler.info(ref)
258 name = info.getName(ref).replace('\xc2\x86', '').replace('\xc2\x87', '')
259 event_name = self.getEventName(info, ref)
260 self.session.nav.playService(ref)
262 # Get next window index
263 self.current_window += 1
264 if self.current_window > 9:
265 self.current_window = 1
268 self.window_refs[self.current_window-1] = ref
270 # Save the event-name and hide the label
271 self["event" + str(self.current_window)].hide()
272 self["event" + str(self.current_window)].setText(event_name)
274 # Show the new video-window
275 self["video" + str(self.current_window)].show()
276 self["video" + str(self.current_window)].decoder = 0
278 # Show the servicename
279 self["channel" + str(self.current_window)].setText(name)
280 self["count"].setText(_("Channel: ") + str(self.current_refidx + 1) + " / " + str(len(self.ref_list)))
284 self.updateTimer.start(1, 1)
286 print "[Mosaic] retval: %d result: %s" % (retval, result)
289 f = open(grab_errorlog, "w")
290 f.write("retval: %d\nresult: %s" % (retval, result))
295 self.session.openWithCallback(self.exit, MessageBox, _("Error while creating screenshot. You need grab version 0.8 or higher!"), MessageBox.TYPE_ERROR, timeout=5)
297 def updateCountdown(self, callback=None):
299 self.updateCountdownLabel()
300 if self.countdown == 0:
301 self.countdown = config.plugins.Mosaic.countdown.value
303 self.makeNextScreenshot()
305 self.updateTimer.start(1000, 1)
307 def updateCountdownLabel(self):
308 self["countdown"].setText("%s %s / %s" % (_("Countdown:"), str(self.countdown), str(config.plugins.Mosaic.countdown.value)))
310 def getEventName(self, info, ref):
311 event = info.getEvent(ref)
312 if event is not None:
313 eventName = event.getEventName()
314 if eventName is None:
320 ################################################
321 # Most stuff stolen from the GraphMultiEPG
325 BouquetSelectorScreen = None
327 def getBouquetServices(bouquet):
329 Servicelist = eServiceCenter.getInstance().list(bouquet)
330 if Servicelist is not None:
332 service = Servicelist.getNext()
333 if not service.valid():
335 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker):
337 services.append(service)
340 def closeBouquetSelectorScreen(ret=None):
341 if BouquetSelectorScreen is not None:
342 BouquetSelectorScreen.close()
344 def openMosaic(bouquet):
345 if bouquet is not None:
346 services = getBouquetServices(bouquet)
348 Session.openWithCallback(closeBouquetSelectorScreen, Mosaic, services)
350 def main(session, servicelist, **kwargs):
354 Servicelist = servicelist
355 global BouquetSelectorScreen
357 bouquets = Servicelist.getBouquetList()
358 if bouquets is not None:
359 if len(bouquets) == 1:
360 openMosaic(bouquets[0][1])
361 elif len(bouquets) > 1:
362 BouquetSelectorScreen = Session.open(BouquetSelector, bouquets, openMosaic, enableWrapAround=True)
364 def Plugins(**kwargs):
365 return PluginDescriptor(name=_("Mosaic"), where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main)