[PluginSort] - fix left/right and move to last
[enigma2-plugins.git] / mosaic / src / plugin.py
1 # -*- coding: UTF-8 -*-
2 # Mosaic by AliAbdul
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
17
18 ################################################
19
20 grab_binary = "/usr/bin/grab"
21 grab_picture = "/tmp/mosaic.jpg"
22 grab_errorlog = "/tmp/mosaic.log"
23
24 config_limits = (3, 30)
25 config.plugins.Mosaic = ConfigSubsection()
26 config.plugins.Mosaic.countdown = ConfigInteger(default=5, limits=config_limits)
27
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'))
30
31 class Mosaic(Screen):
32         PLAY = 0
33         PAUSE = 1
34         
35         desktop = getDesktop(0)
36         size = desktop.size()
37         width = size.width()
38         height = size.height()
39         windowWidth = width / 4
40         windowHeight = height / 4
41         
42         positions = []
43         x = 80
44         y = 50
45         for i in range(1, 10):
46                 positions.append([x, y])
47                 x += windowWidth
48                 x += ((width - 160) - (windowWidth * 3)) / 2
49                 if (i == 3) or (i == 6):
50                         y = y + windowHeight + 20
51                         x = 80
52         
53         skin = ""
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)
101
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)
105
106         def __init__(self, session, services):
107                 Screen.__init__(self, session)
108                 
109                 self.session = session
110                 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
111                 self.consoleCmd = ""
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
119                 self.working = False
120                 self.state = self.PLAY
121                 
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()
135                 
136                 self["actions"] = NumberActionMap(["MosaicActions"],
137                         {
138                                 "ok": self.exit,
139                                 "cancel": self.closeWithOldService,
140                                 "green": self.play,
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
153                         }, prio=-1)
154                 
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)
160
161         def checkGrab(self):
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)
175
176         def exit(self, callback=None):
177                 self.deleteConsoleCallbacks()
178                 self.close()
179
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]
187
188         def closeWithOldService(self):
189                 self.session.nav.playService(self.oldService)
190                 self.deleteConsoleCallbacks()
191                 self.close()
192
193         def numberPressed(self, number):
194                 ref = self.window_refs[number-1]
195                 if ref is not None:
196                         self.session.nav.playService(ref)
197                         self.deleteConsoleCallbacks()
198                         self.close()
199
200         def play(self):
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)
205
206         def pause(self):
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)
211
212         def countdownPlus(self):
213                 self.changeCountdown(1)
214
215         def countdownMinus(self):
216                 self.changeCountdown(-1)
217
218         def changeCountdown(self, direction):
219                 if self.working == False:
220                         configNow = config.plugins.Mosaic.countdown.value
221                         configNow += direction
222                         
223                         if configNow < config_limits[0]:
224                                 configNow = config_limits[0]
225                         elif configNow > config_limits[1]:
226                                 configNow = config_limits[1]
227                         
228                         config.plugins.Mosaic.countdown.value = configNow
229                         config.plugins.Mosaic.countdown.save()
230                         
231                         self.updateCountdownLabel()
232
233         def makeNextScreenshot(self):
234                 # Grab video
235                 if not self.Console:
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)
239
240         def showNextScreenshot(self, result, retval, extra_args):
241                 if retval == 0:
242                         # Show screenshot in the current window
243                         pic = LoadPixmap(grab_picture)
244                         self["window" + str(self.current_window)].instance.setPixmap(pic)
245                 
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()
249                 
250                         # Get next ref
251                         self.current_refidx += 1
252                         if self.current_refidx > (len(self.ref_list) -1):
253                                 self.current_refidx = 0
254                 
255                         # Play next ref
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)
261                 
262                         # Get next window index
263                         self.current_window += 1
264                         if self.current_window > 9:
265                                 self.current_window = 1
266                 
267                         # Save the ref
268                         self.window_refs[self.current_window-1] = ref
269                 
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)
273                 
274                         # Show the new video-window
275                         self["video" + str(self.current_window)].show()
276                         self["video" + str(self.current_window)].decoder = 0
277                 
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)))
281                 
282                         # Restart timer
283                         self.working = False
284                         self.updateTimer.start(1, 1)
285                 else:
286                         print "[Mosaic] retval: %d result: %s" % (retval, result)
287                         
288                         try:
289                                 f = open(grab_errorlog, "w")
290                                 f.write("retval: %d\nresult: %s" % (retval, result))
291                                 f.close()
292                         except:
293                                 pass
294                         
295                         self.session.openWithCallback(self.exit, MessageBox, _("Error while creating screenshot. You need grab version 0.8 or higher!"), MessageBox.TYPE_ERROR, timeout=5)
296
297         def updateCountdown(self, callback=None):
298                 self.countdown -= 1
299                 self.updateCountdownLabel()
300                 if self.countdown == 0:
301                         self.countdown = config.plugins.Mosaic.countdown.value
302                         self.working = True
303                         self.makeNextScreenshot()
304                 else:
305                         self.updateTimer.start(1000, 1)
306
307         def updateCountdownLabel(self):
308                 self["countdown"].setText("%s %s / %s" % (_("Countdown:"), str(self.countdown), str(config.plugins.Mosaic.countdown.value)))
309
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:
315                                 eventName = ""
316                 else:
317                         eventName = ""
318                 return eventName
319
320 ################################################
321 # Most stuff stolen from the GraphMultiEPG
322
323 Session = None
324 Servicelist = None
325 BouquetSelectorScreen = None
326
327 def getBouquetServices(bouquet):
328         services = []
329         Servicelist = eServiceCenter.getInstance().list(bouquet)
330         if Servicelist is not None:
331                 while True:
332                         service = Servicelist.getNext()
333                         if not service.valid():
334                                 break
335                         if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker):
336                                 continue
337                         services.append(service)
338         return services
339
340 def closeBouquetSelectorScreen(ret=None):
341         if BouquetSelectorScreen is not None:
342                 BouquetSelectorScreen.close()
343
344 def openMosaic(bouquet):
345         if bouquet is not None:
346                 services = getBouquetServices(bouquet)
347                 if len(services):
348                         Session.openWithCallback(closeBouquetSelectorScreen, Mosaic, services)
349
350 def main(session, servicelist, **kwargs):
351         global Session
352         Session = session
353         global Servicelist
354         Servicelist = servicelist
355         global BouquetSelectorScreen
356         
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)
363
364 def Plugins(**kwargs):
365         return PluginDescriptor(name=_("Mosaic"), where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main)