Changes to use now grab v0.8. Thanks to seddi for the new version
[enigma2-plugins.git] / mosaic / src / plugin.py
1 # -*- coding: UTF-8 -*-
2 # Mosaic by AliAbdul
3 # needs the aio-screengrabber v0.8 by seddi
4 from Components.ActionMap import NumberActionMap
5 from Components.config import config, ConfigSubsection, ConfigInteger
6 from Components.Console import Console
7 from Components.Label import Label
8 from Components.Language import language
9 from Components.Pixmap import Pixmap
10 from Components.VideoWindow import VideoWindow
11 from enigma import eServiceCenter, eServiceReference, eTimer, loadPNG
12 from os import environ
13 from Plugins.Plugin import PluginDescriptor
14 from Screens.ChannelSelection import BouquetSelector
15 from Screens.MessageBox import MessageBox
16 from Screens.Screen import Screen
17 from Tools.Directories import fileExists, resolveFilename, SCOPE_SKIN_IMAGE, SCOPE_LANGUAGE
18 from Tools.LoadPixmap import LoadPixmap
19 import gettext
20
21 ################################################
22
23 grab_binary = "/usr/bin/grab"
24 grab_picture = "/tmp/mosaic.jpg"
25 grab_errorlog = "/tmp/mosaic.log"
26
27 config_limits = (3, 30)
28 config.plugins.Mosaic = ConfigSubsection()
29 config.plugins.Mosaic.countdown = ConfigInteger(default=5, limits=config_limits)
30
31 playingIcon = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/ico_mp_play.png'))
32 pausedIcon = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/ico_mp_pause.png'))
33
34 ################################################
35
36 lang = language.getLanguage()
37 environ["LANGUAGE"] = lang[:2]\r
38 gettext.bindtextdomain("enigma2", resolveFilename(SCOPE_LANGUAGE))\r
39 gettext.textdomain("enigma2")\r
40 gettext.bindtextdomain("Mosaic", resolveFilename(SCOPE_LANGUAGE))
41
42 def _(txt):\r
43         t = gettext.dgettext("Mosaic", txt)\r
44         if t == txt:\r
45                 t = gettext.gettext(txt)\r
46         return t
47
48 ################################################
49
50 class Mosaic(Screen):
51         PLAY = 0
52         PAUSE = 1
53
54         skin = """
55         <screen position="0,0" size="720,576" title="Mosaic" flags="wfNoBorder" backgroundColor="#ffffff" >
56                 <widget name="playState" position="55,55" size="16,16" alphatest="on" />
57                 <eLabel position="78,54" size="180,144" />
58                 <eLabel position="274,54" size="180,144" />
59                 <eLabel position="470,54" size="180,144" />
60                 <eLabel position="78,221" size="180,144" />
61                 <eLabel position="274,221" size="180,144" />
62                 <eLabel position="470,221" size="180,144" />
63                 <eLabel position="78,388" size="180,144" />
64                 <eLabel position="274,388" size="180,144" />
65                 <eLabel position="470,388" size="180,144" />
66                 <widget name="channel1" position="80,32" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
67                 <widget name="channel2" position="276,32" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
68                 <widget name="channel3" position="472,32" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
69                 <widget name="channel4" position="80,198" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
70                 <widget name="channel5" position="276,198" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
71                 <widget name="channel6" position="472,198" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
72                 <widget name="channel7" position="80,366" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
73                 <widget name="channel8" position="276,366" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
74                 <widget name="channel9" position="472,366" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
75                 <widget name="window1" position="78,54" zPosition="1" size="180,144" />
76                 <widget name="window2" position="274,54" zPosition="1" size="180,144" />
77                 <widget name="window3" position="470,54" zPosition="1" size="180,144" />
78                 <widget name="window4" position="78,221" zPosition="1" size="180,144" />
79                 <widget name="window5" position="274,221" zPosition="1" size="180,144" />
80                 <widget name="window6" position="470,221" zPosition="1" size="180,144" />
81                 <widget name="window7" position="78,388" zPosition="1" size="180,144" />
82                 <widget name="window8" position="274,388" zPosition="1" size="180,144" />
83                 <widget name="window9" position="470,388" zPosition="1" size="180,144" />
84                 <widget name="video1" position="78,54" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
85                 <widget name="video2" position="274,54" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
86                 <widget name="video3" position="470,54" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
87                 <widget name="video4" position="78,221" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
88                 <widget name="video5" position="274,221" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
89                 <widget name="video6" position="470,221" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
90                 <widget name="video7" position="78,388" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
91                 <widget name="video8" position="274,388" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
92                 <widget name="video9" position="470,388" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
93                 <widget name="event1" position="78,54" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
94                 <widget name="event2" position="274,54" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
95                 <widget name="event3" position="470,54" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
96                 <widget name="event4" position="78,221" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
97                 <widget name="event5" position="274,221" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
98                 <widget name="event6" position="470,221" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
99                 <widget name="event7" position="78,388" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
100                 <widget name="event8" position="274,388" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
101                 <widget name="event9" position="470,388" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
102                 <widget name="countdown" position="80,535" size="175,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
103                 <widget name="count" position="472,535" size="175,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" halign="right" />
104         </screen>"""
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.Console = Console()
112                 self.serviceHandler = eServiceCenter.getInstance()
113                 self.ref_list = services
114                 self.window_refs = [None, None, None, None, None, None, None, None, None]
115                 self.current_refidx = 0
116                 self.current_window = 1
117                 self.countdown = config.plugins.Mosaic.countdown.value
118                 self.working = False
119                 self.state = self.PLAY
120                 
121                 self["playState"] = Pixmap()
122                 for i in range(1, 10):
123                         self["window" + str(i)] = Pixmap()
124                         self["video" + str(i)] = VideoWindow(decoder = 0)
125                         self["video" + str(i)].hide()
126                         self["channel" + str(i)] = Label("")
127                         self["event" + str(i)] = Label("")
128                         self["event" + str(i)].hide()
129                 self["video1"].decoder = 0
130                 self["video1"].show()
131                 self["countdown"] = Label()
132                 self.updateCountdownLabel()
133                 self["count"] = Label()
134                 
135                 self["actions"] = NumberActionMap(["MosaicActions"],
136                         {
137                                 "ok": self.close,
138                                 "cancel": self.closeWithOldService,
139                                 "green": self.play,
140                                 "yellow": self.pause,
141                                 "channelup": self.countdownPlus,
142                                 "channeldown": self.countdownMinus,
143                                 "1": self.numberPressed,
144                                 "2": self.numberPressed,
145                                 "3": self.numberPressed,
146                                 "4": self.numberPressed,
147                                 "5": self.numberPressed,
148                                 "6": self.numberPressed,
149                                 "7": self.numberPressed,
150                                 "8": self.numberPressed,
151                                 "9": self.numberPressed
152                         }, prio=-1)
153                 
154                 self.updateTimer = eTimer()
155                 self.updateTimer.timeout.get().append(self.updateCountdown)
156                 self.checkTimer = eTimer()
157                 self.checkTimer.timeout.get().append(self.checkGrab)
158                 self.checkTimer.start(500, 1)
159
160         def checkGrab(self):
161                 if fileExists(grab_binary):
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                         
174                         # Start updating the video-screenshots
175                         self.updateTimer.start(1, 1)
176                 else:
177                         self.session.openWithCallback(self.exit, MessageBox, _("%s does not exist!") % grab_binary, MessageBox.TYPE_ERROR, timeout=5)
178
179         def exit(self, callback=None):
180                 self.close()
181
182         def closeWithOldService(self):
183                 self.session.nav.playService(self.oldService)
184                 self.close()
185
186         def numberPressed(self, number):
187                 ref = self.window_refs[number-1]
188                 if ref is not None:
189                         self.session.nav.playService(ref)
190                         self.close()
191
192         def play(self):
193                 if self.working == False and self.state == self.PAUSE:
194                         self.state = self.PLAY
195                         self.updateTimer.start(1000, 1)
196                         self["playState"].instance.setPixmap(playingIcon)
197
198         def pause(self):
199                 if self.working == False and self.state == self.PLAY:
200                         self.state = self.PAUSE
201                         self.updateTimer.stop()
202                         self["playState"].instance.setPixmap(pausedIcon)
203
204         def countdownPlus(self):
205                 self.changeCountdown(1)
206
207         def countdownMinus(self):
208                 self.changeCountdown(-1)
209
210         def changeCountdown(self, direction):
211                 if self.working == False:
212                         configNow = config.plugins.Mosaic.countdown.value
213                         configNow += direction
214                         
215                         if configNow < config_limits[0]:
216                                 configNow = config_limits[0]
217                         elif configNow > config_limits[1]:
218                                 configNow = config_limits[1]
219                         
220                         config.plugins.Mosaic.countdown.value = configNow
221                         config.plugins.Mosaic.countdown.save()
222                         
223                         self.updateCountdownLabel()
224
225         def makeNextScreenshot(self):
226                 # Grab video
227                 if not self.Console:
228                         self.Console = Console()
229                 cmd = "%s -v -r 180 -l -j 100 %s" % (grab_binary, grab_picture)
230                 self.Console.ePopen(cmd, self.showNextScreenshot)
231
232         def isStillRunning(self):
233                 try:
234                         instance = self.instance
235                         return True
236                 except:
237                         return False
238
239         def showNextScreenshot(self, result, retval, extra_args):
240                 if not self.isStillRunning():
241                         # If the grab is working and the user exits the plugin there is no instance any more...
242                         # Console.ePopen gives us a callback and this can't be handled any more!
243                         # Should be there a fix in the Console class?
244                         pass
245                 elif retval == 0:
246                         # Show screenshot in the current window
247                         pic = LoadPixmap(grab_picture)
248                         self["window" + str(self.current_window)].instance.setPixmap(pic)
249                 
250                         # Hide current video-window and show the running event-name
251                         self["video" + str(self.current_window)].hide()
252                         self["event" + str(self.current_window)].show()
253                 
254                         # Get next ref
255                         self.current_refidx += 1
256                         if self.current_refidx > (len(self.ref_list) -1):
257                                 self.current_refidx = 0
258                 
259                         # Play next ref
260                         ref = self.ref_list[self.current_refidx]
261                         info = self.serviceHandler.info(ref)
262                         name = info.getName(ref).replace('\xc2\x86', '').replace('\xc2\x87', '')
263                         event_name = self.getEventName(info, ref)
264                         self.session.nav.playService(ref)
265                 
266                         # Get next window index
267                         self.current_window += 1
268                         if self.current_window > 9:
269                                 self.current_window = 1
270                 
271                         # Save the ref
272                         self.window_refs[self.current_window-1] = ref
273                 
274                         # Save the event-name and hide the label
275                         self["event" + str(self.current_window)].hide()
276                         self["event" + str(self.current_window)].setText(event_name)
277                 
278                         # Show the new video-window
279                         self["video" + str(self.current_window)].show()
280                         self["video" + str(self.current_window)].decoder = 0
281                 
282                         # Show the servicename
283                         self["channel" + str(self.current_window)].setText(name)
284                         self["count"].setText(_("Channel: ") + str(self.current_refidx + 1) + " / " + str(len(self.ref_list)))
285                 
286                         # Restart timer
287                         self.working = False
288                         self.updateTimer.start(1, 1)
289                 else:
290                         print "[Mosaic] retval: %d result: %s" % (retval, result)
291                         
292                         try:
293                                 f = open(grab_errorlog, "w")
294                                 f.write("retval: %d\nresult: %s" % (retval, result))
295                                 f.close()
296                         except:
297                                 pass
298                         
299                         self.session.openWithCallback(self.exit, MessageBox, _("Error while creating screenshot. You need grab version 0.8 or higher!"), MessageBox.TYPE_ERROR, timeout=5)
300
301         def updateCountdown(self, callback=None):
302                 self.countdown -= 1
303                 self.updateCountdownLabel()
304                 if self.countdown == 0:
305                         self.countdown = config.plugins.Mosaic.countdown.value
306                         self.working = True
307                         self.makeNextScreenshot()
308                 else:
309                         self.updateTimer.start(1000, 1)
310
311         def updateCountdownLabel(self):
312                 self["countdown"].setText("%s %s / %s" % (_("Countdown:"), str(self.countdown), str(config.plugins.Mosaic.countdown.value)))
313
314         def getEventName(self, info, ref):
315                 event = info.getEvent(ref)
316                 if event is not None:
317                         eventName = event.getEventName()
318                         if eventName is None:
319                                 eventName = ""
320                 else:
321                         eventName = ""
322                 return eventName
323
324 ################################################
325 # Most stuff stolen from the GraphMultiEPG
326
327 Session = None
328 Servicelist = None
329 BouquetSelectorScreen = None
330
331 def getBouquetServices(bouquet):
332         services = []
333         Servicelist = eServiceCenter.getInstance().list(bouquet)
334         if Servicelist is not None:
335                 while True:
336                         service = Servicelist.getNext()
337                         if not service.valid():
338                                 break
339                         if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker):
340                                 continue
341                         services.append(service)
342         return services
343
344 def closeBouquetSelectorScreen(ret=None):
345         if BouquetSelectorScreen is not None:
346                 BouquetSelectorScreen.close()
347
348 def openMosaic(bouquet):
349         if bouquet is not None:
350                 services = getBouquetServices(bouquet)
351                 if len(services):
352                         Session.openWithCallback(closeBouquetSelectorScreen, Mosaic, services)
353
354 def main(session, servicelist, **kwargs):
355         global Session
356         Session = session
357         global Servicelist
358         Servicelist = servicelist
359         global BouquetSelectorScreen
360         
361         bouquets = Servicelist.getBouquetList()
362         if bouquets is not None:
363                 if len(bouquets) == 1:
364                         self.openMosaic(bouquets[0][1])
365                 elif len(bouquets) > 1:
366                         BouquetSelectorScreen = Session.open(BouquetSelector, bouquets, openMosaic, enableWrapAround=True)
367
368 def Plugins(**kwargs):
369         return PluginDescriptor(name=_("Mosaic"), where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main)